; ; File: RBVDriver.a ; ; Contains: The Mac OS Video Driver code that supports RBV-based built-in video ; systems. ; ; Written by: David Fung ; ; Copyright: © 1987-1992 by Apple Computer, Inc. All rights reserved. ; ; Change History (most recent first): ; ; 11/5/92 SWC Changed VideoEqu.a->Video.a and ShutdownEqu.a->Shutdown.a. ; 11/2/92 kc Don't include SonicEqu.a. ; <1> 10/6/92 GDW New location for ROMLink tool. ; 09-03-92 jmp (jmp,H2) Corrected .s vs. non-.s branches and odd-alignment ; problems. ; 5/16/92 kc Roll in Horror Changes. Comments follow: ; 12/11/90 HJR Integration of Tim and V8 Video into the Terror Project. ; 6/12/90 JJ Add support for Elsie Apple II emulation video modes. ; 4/30/90 JJ Minor formatting changes. ;

4/3/90 CV Moved the search string to find the video driver overpatch to ; 'DeclDataRBV.a'. ;

3/13/90 CV Improving Erickson overpatch version of the driver. ; Added prime routine which finishes up install of overpatch ; RBVDriver. ; <5> 9/18/91 JSM Cleanup header. ; <4> 4/3/90 DF Moved GDFlags bit flag equates to RBVDepVideoEqu.a where they ; are shared by ElsieDriver.a ; <3> 2/14/90 DAF Changed Open routine to remove dependency on Is16MHz flag word ; <2> 2/13/90 DAF Corrected interrupt level changing code for Erickson. ; 2/13/90 DAF Corrected interrupt level changing code for Erickson. ; <2.0> 7/11/89 DAF FOR AURORA BUILD - Minor code review changes in Open ; 7/11/89 DAF Incorporated code review changes. Added flag detection of ; non-16MHz machines. ; <1.9> 6/30/89 DAF Implemented GetEntries, changed to non-dynamic configuration. ; Added code for RBVSE/30's ; 6/28/89 DAF Changed driver name. Added GetEntries. Cleaned up slot interrupt ; allocation stuff. Added 16/25MHz detection and mode checking. ; <1.8> 6/11/89 GGD Get Frame Buffer base from rom tables. ; <1.7> 5/16/89 DAF Fixed GetDefaultMode status call. Updated interrupt level code. ; <1.6> 5/15/89 DAF Many changes. Added fasterAurora fix. Corrected WaitVSync. Added ; SetDefaultMode. Updated setup for new DeclDataRBV ; 5/15/89 DAF Fixed GetDefaultMode. Updated the interrupt level changes to be ; more efficient. ; 5/2/89 DAF More Universal ROM support. Updated interrupt manipulations to ; be more graceful. Reordered private storage. Added Faster Aurora ; time manager hack to overcome RBV interrupt problems. ; <¥1.5> 4/15/89 DAF Made a register mistake. Fixed it now.. ; <¥1.4> 4/15/89 DAF Improved SetGamma, eliminated SetDisable (obsolete). Default ; gamma from gamma directory. Updated hardware accesses for ; universal ROM ; 4/15/89 DAF Updated SetGamma, removed SetDeactive. Tweaked SetGamma to get ; along with Dimmer. Used lo-mems for Universal ROM support. ; <1.3> 3/6/89 GGD Changed RBV register usage now that equates are offsets from ; RBVBase, instead ; <1.2> 2/27/89 DAF Updated scrn resource invalidation in SetDeactive ; <1.1> 11/17/88 DAF Set VDAC pixel read mask in SetEntries control call ; 11/17/88 DAF Set vDAC pixel mask field in SetEntries ; <1.0> 11/11/88 DAF Adding to EASE for the first time. ; 10/26/88 DAF New today ; STRING C PRINT OFF LOAD 'StandardEqu.d' INCLUDE 'DockingEqu.a' INCLUDE 'EgretEqu.a' INCLUDE 'GestaltEqu.a' INCLUDE 'GestaltPrivateEqu.a' INCLUDE 'HardwarePrivateEqu.a' INCLUDE 'IOPrimitiveEqu.a' INCLUDE 'PowerPrivEqu.a' INCLUDE 'ROMEqu.a' INCLUDE 'Video.a' INCLUDE 'SlotMgrEqu.a' INCLUDE 'ShutDown.a' ; INCLUDE 'SonicEqu.a' INCLUDE 'UniversalEqu.a' INCLUDE 'DepVideoEqu.a' PRINT ON SEG '_sRBVDriver' BLANKS ON STRING ASIS MACHINE MC68020 ;------------------------------------------------------------------- ; Local Vars, definitions, etc.... ;------------------------------------------------------------------- ; This is device storage which is stored in the dCtlStorage field of the DCE. saveBaseAddr EQU 0 ; the screen base address saveSQElPtr EQU saveBaseAddr+4 ; the SQ element pointer (for _SIntRemove). saveGammaPtr EQU saveSQElPtr+4 ; the pointer to the Gamma correction table saveVDACBase EQU saveGammaPtr+4 ; the base addr of the VDAC TTask EQU saveVDACBase+4 ; a time manager queue block GFlags EQU TTask+tmQSize ; flags word saveMode EQU GFlags+2 ; the current mode setting saveID EQU saveMode+2 ; monitor type ID saveGamDispPtr EQU saveID+2 ; pointer to gamma table block dCtlSize EQU saveGamDispPtr+4 ; size of dCtlStorage LRBVDriver MAIN EXPORT ;------------------------------------------------------------------- ; Video Driver Header ;------------------------------------------------------------------- VidDrvr DC.W $4C00 ; ctl,status,needsLock DC.W 0,0,0 ; not an ornament ; normal entry point offset table for undivided driver, and also the dispatch table ; for the overpatch-resident driver. DC.W VideoOpen-VidDrvr ; open routine DC.W VidDrvr-VidDrvr ; no prime in normal video drivers DC.W VideoCtl-VidDrvr ; control DC.W VideoStatus-VidDrvr ; status DC.W VideoClose-VidDrvr ; close STRING Pascal VideoTitle DC.B '.Display_Video_Apple_RBV1' ; same name for 25 & 16MHz versions STRING ASIS ALIGN 2 ; make sure we're aligned DC.W 1 ; version = 0 ; ; CountTbl is the number of entries to change in each mode, set up to be indexed ; via the modeID. ; CountTbl DC.B $01,$03,$0F,$FF ; ; The active color table locations in the Cobra system is non-inverted and linearly ; ascending in all screen depths. However, it always places the last active elemnt ; at the end of the CLUT rather than at the beginning (sigh). This table holds ; the offset to the active section of the vDAC for each mode. ; StartTbl DC.B 254,252,240,0 ALIGN 2 ; correct odd alignment ********************************************************************** * * VideoOpen allocates private storage for the device in the DCE and locks * it down for perpetuity. Also, install the interrupt handler and enable * the interrupts. * * Entry: A0 = param block pointer * A1 = DCE pointer * * Locals: A3 = pointer to private storage * ********************************************************************** WITH VDPageInfo,SlotIntQElement VideoOpen ; ; Allocate private storage (since block is CLEAR, GFlags are zeroed) and get ; a pointer to it in A3 ; MOVEQ #dCtlSize,D0 ; get size of parameters _ResrvMem ,SYS ; make room as low as possible MOVEQ #dCtlSize,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 DCE _HLock ; and lock it down forever (this includes a Time Mgr QElem) MOVE.L (A0),A3 ; get a pointer to it ; ; remember the VDAC base address since it's hard to look up. We don't need to test ; for it since we couldn't get here unless there was an RBV. By the way, we ; don't need to remember the RBV base, since there's a lomem for it. ; WITH ProductInfo,DecoderInfo MOVE.L UnivInfoPtr,A0 ; get a pointer to universal data ADD.L DecoderInfoPtr(A0),A0 ; point to the base address table MOVE.L VDACAddr(A0),saveVDACBase(A3) ; save pointer ENDWITH ; ; Initialize and install the time manager task for the RBV interrupt problem. See the ; comment at the end of the driver for more information about the nature of the RBV ; interrupt problem. ; LEA TimeHandler,A0 ; get a pointer to the task code MOVE.L A0,tmAddr+TTask(A3) ; put it in the time task LEA TTask(A3),A0 ; get a pointer to the queue element _InsTime ; install the task ; ; 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 Z-bit cleared. MOVEQ #sqHDSize,D0 ; allocate a slot queue element _NewPtr ,SYS,CLEAR ; get it from system heap cleared BNE OpError ; if not allocated, return bad MOVE.L A0,saveSQElPtr(A3) ; save the SQ element pointer. BSR EnableVGuts ; do it BNE OpError ; ; ; read the RBV to find out what kind of monitor we have. If it's a Portrait display (ID=1) ; then set the monochrome only flag. To have gotten here, we MUST have a valid video device. ; We also know that if we read 7 as the monitor type, that we must be in an SE display machine. ; If we weren't, then no display was connected, and this driver would not be open. ; MOVE.L RBV,A0 ; get the RBV base address MOVE.B RvMonP(A0),D0 ; get the monitor type LSR.B #3,D0 ; shift Monitor ID into the low bits AND.W #7,D0 ; lo 3 bits only MOVE.W D0,saveID(A3) ; remember it for later CMP.W #FPIdMono,D0 ; is this the ID for the monochrome portrait display? BNE.S @Box ; nope, so continue OR.B #$A0,GFlags(A3) ; yup, so turn on the luminence map and IsMono bits ; ; see if we are on a 16MHz CPU and set the GFlag appropriately. This limits the valid ; screen depths on certain monitors. Since there are no firm plans for SE/30 type machines ; based on this generation, we don't do any special detection here. ; @Box CMP.B #BoxAuroraSE16,BoxFlag ; is this a 16MHz RBV machine? BNE.S @Gamma ; no, so continue BSET #Is16,GFlags(A3) ; turn on flag bits ; ; load the default gamma table from the slot resource list ; @Gamma 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 <2.0> CLR.B spExtDev(A0) ; <2.0> CLR.B spHWDev(A0) ; ???? _sRsrcInfo ; get the spsPointer <2.0> MOVE.B #sGammaDir,spID(A0) ; look for the gamma directory _sFindStruct ; get that baby BNE.S OpError ; if failed, then quit MOVE.B #$80,spID(A0) ; get the default gamma table, (always 128) _sGetBlock ; we can use this since we want it on the sys heap BNE.S OpError ; if failed, then quite ; ; skip over header ; MOVE.L spResult(A0),A0 ; point to head of the block MOVE.L A0,saveGamDispPtr(A3) ; remember this block for Close 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.L #0,D0 ; round it off MOVE.L D0,saveGammaPtr(A3) ; put it in private storage ADDA #spBlockSize,SP ; release the parameter block ; ; all done! ; @AllDone MOVEQ #0,D0 ; no error EndOpen RTS ; return OpError LEA TTask(A3),A0 ; point to the time manager queue element _RmvTime ; delete it from the queue (no errors) 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 ADDA #spBlockSize,SP ; release the spBlock BRA.S EndOpen ENDWITH ********************************************************************** * * Video Driver Control Call Handler. There are nine calls: * * (0) Reset (VAR mode, page: INTEGER; VAR BaseAddr: Ptr); * (1) KillIO * (2) SetMode(mode, page: INTEGER; VAR BaseAddr: Ptr); * (3) SetEntries ( Table: Ptr; Start,Count : integer ); * (4) SetGamma ( Table : Ptr ); * (5) GrayPage (page); * (6) SetGray (csMode = 0 for color, 1 for gray) * (7) SetInterrupt ( csMode = 0 for enable, 1 for disable) * (8) DirectSetEntries (not implemented) * (9) SetDefaultMode * * Entry: A0 = param block pointer * A1 = DCE pointer * Uses: A2 = cs parameters (ie. A2 <- csParam(A0)) (must be preserved) * A3 = scratch (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 ; VideoCtl MOVE.L A0,-(SP) ; save work registers (A0 is saved because it is used by ExitDrvr) MOVE.W csCode(A0),D0 ; get the opCode MOVE.L csParam(A0),A2 ; A2 <- Ptr to control parameters MOVE.L dCtlStorage(A1),A3 MOVE.L (A3),A3 ; get pointer to private storage CMP.W #9,D0 ; IF csCode NOT IN [0..9] THEN BHI.S CtlBad ; Error, csCode out of bounds MOVE.W CtlJumpTbl(PC,D0.W*2),D0 ; Get the relative offset to the routine JMP CtlJumpTbl(PC,D0.W) ; GOTO the proper routine CtlJumpTbl DC.W VidReset-CtlJumpTbl ; $00 => VidReset DC.W CtlGood-CtlJumpTbl ; $01 => CtlGood (no async routines here) DC.W SetVidMode-CtlJumpTbl ; $02 => SetVidMode DC.W SetEntries-CtlJumpTbl ; $03 => SetEntries DC.W SetGamma-CtlJumpTbl ; $04 => SetGamma DC.W GrayPage-CtlJumpTbl ; $05 => GrayPage DC.W SetGray-CtlJumpTbl ; $06 => SetGray DC.W SetInterrupt-CtlJumpTbl ; $07 => SetInterrupt DC.W CtlBad-CtlJumpTbl ; $08 => DirectSetEntries DC.W SetDefaultMode-CtlJumpTbl ; $09 => SetDefaultMode CtlBad MOVEQ #controlErr,D0 ; else say we don't do this one BRA.S CtlDone ; and return CtlGood MOVEQ #noErr,D0 ; return no error CtlDone MOVE.L (SP)+,A0 ; restore registers. BRA ExitDrvr VidReset ;--------------------------------------------------------------------- ; ; Reset the card to its default ; ;--------------------------------------------------------------------- MOVE #FirstVidMode,csMode(A2) ; return default mode MOVE #FirstVidMode,saveMode(A3) ; remember FirstVidMode as the requested mode MOVE #1,D1 ; get default depth in D1 @2 MOVEQ #0,D0 ; get page in D0 MOVE D0,csPage(A2) ; return the page BSR RBVSetDepth ; set the depth from D1 MOVE.L saveBaseAddr(A3),csBaseAddr(A2) ; return the base address BSR GrayScreen ; paint the screen gray BRA.S CtlGood ; => no error SetVidMode ;--------------------------------------------------------------------- ; ; Set the card to the specified mode and page. ; If either is invalid, returns badMode error. ; ; If the card is already set to the specified mode, then do nothing. ; ;--------------------------------------------------------------------- MOVE.W csMode(A2),D1 ; D1 = mode BSR ChkMode ; check mode BNE.S CtlBad ; => not a valid mode CMP.W #0,csPage(A2) ; if not page zero, then return an error BNE.S CtlBad ; => not a valid page ; Only set the mode if it has changed ; RBVSetDepth updates the saved data in the dCtlStorage SetEm MOVE.W csMode(A2),D2 ; get the mode (again) CMP saveMode(A3),D2 ; has the mode changed? BEQ.S ModeOK1 ; if not, then we're done ; 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 MOVE.L saveGammaPtr(A3),A0 ; get pointer to gamma data structure MOVE GFormulaSize(A0),D3 ; get the size of formula data MOVE.W GDataCnt(A0),D2 ; get number of gamma entries LSR.W #1,D2 ; divide by two to find midpoint LEA GFormulaData(A0),A0 ; point to formula data ADD D3,A0 ; first correction table starts here MOVE.B (A0,D2),D3 ; get corrected gray from middle of red table MOVE.W SR,-(SP) ; preserve the status register <2> MOVEQ #7,D0 ; get mask in D0 <2> AND.B (SP),D0 ; get the interrupt level <2> SUBQ.B #2,D0 ; <2> BGE.S @OK ; if ³, then don't change <2> ORI.W #$0200,SR ; raise above level-2 <2> ANDI.W #$FAFF,SR ; make it level-2 <2> @OK BSR WaitVSync ; wait for next blanking period MOVE.L saveVDACBase(A3),A0 ; get the VDAC base addr ADDA #vDACwDataReg,A0 ; A0 <- base address of device. CLR.B vDACwAddReg-vDACwDataReg(A0) ; set the CLUT start address MOVE #$FF,D2 ; get count @Repeat MOVE.B D3,(A0) ; put red MOVE.B D3,(A0) ; put green MOVE.B D3,(A0) ; put blue DBRA D2,@Repeat ; MOVE (SP)+,SR ; restore the status reg BSR RBVSetDepth ; set the depth, get rowbytes ModeOK1 NoChange MOVE.L saveBaseAddr(A3),csBaseAddr(A2) ; return the base address BRA CtlGood ; => return no error SetEntries ;--------------------------------------------------------------------- ; ; 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 DCE ; A2 = Ptr to cs parameter record ; A3 = Ptr to private data, later to VDAC hardware base ; 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. MOVE.L csTable(A2),D0 ; Check for a nil pointer BEQ CtlBad MOVEM.L A1/A4-A6/D4-D7,-(SP) ; save registers for gamma MOVE.W GFlags(A3),D5 ; get GFlags word in D5 CMP.W #-1,csStart(A2) ; is it sequence mode? BEQ.S @5 ; nope, it's index BSET #UseSeq,D5 ; if sequence then set bit (it's always clear in GFlags) @5 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 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 ADD #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 index LEA CountTbl,A1 ; point to little table of counts MOVE.B (A1,D1),D6 ; pick the zero-based byte LEA StartTbl,A1 ; point to table of vDAC start positions for each mode MOVE.B (A1,D1),D5 ; hide this byte in lo-half of D5 ; ; 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 CMP.B D6,D3 ; is it in the allowable range? BHI BadExit ; if outside, then exit w/bad result MOVE.L D3,D4 ; make a copy of the table size (zero-based!) ADDQ #1,D4 ; make it a counting number BTST #UseSeq,D5 ; are we in index or sequential mode? BEQ.S @isIndex ; if equal, we're indexed @isSeq MULU #3,D4 ; multiply times 3 for sequential mode BRA.S @allocIt ; and continue @isIndex ASL #2,D4 ; multiply times 4 (always less than 1024) @allocIt SUB.W D4,SP ; allocate the buffer ; ; construct the stack version of the color table. It looks like a color table, but each of the ; four components is only eight bits (rather than 16). ; MOVE.L SP,A0 ; copy the stack buffer pointer MOVE.L csTable(A2),A1 ; get colorSpec pointer in A1 ; write the index if in indexed mode. If in sequential eight 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 ADD.B D5,D0 ; add the screen depth clut offset MOVE.B D0,(A0)+ ; put in stack table @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 ; 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},D1 ; get gChanWidth bits for gamma table lookup MOVE.W D1,D0 ; copy into red register MOVE.W D1,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.W (A0)+ ; write black for green and blue 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 ; ; OK, the stack table is set up. Now let's load the hardware. ; MOVE.W csCount(A2),D3 ; get the count again MOVE.L saveVDACBase(A3),A3 ; get VDAC base from driver privates MOVE.B #$FF,vDACPixRdMask(A3) ; enable all bits in the pixel mask LEA vDACwDataReg(A3),A3 ; point to vDAC data write register MOVE.W SR,-(SP) ; preserve the status register <2> MOVEQ #7,D0 ; get mask in D0 <2> AND.B (SP),D0 ; get the interrupt level <2> SUBQ.B #2,D0 ; <2> BGE.S @OK ; if ³, then don't change <2> ORI.W #$0200,SR ; raise above level-2 <2> ANDI.W #$FAFF,SR ; make it level-2 <2> @OK BSR WaitVSync ; wait for next blanking period (preserves A0) LEA 2(SP),A0 ; point to the stack buffer again BTST #UseSeq,D5 ; is it sequence mode? BNE.S SeqWrite ; yup, sequence mode, so go ; ; here's a loop that actually writes to the hardware when in indexed mode ; IndexWrite MOVE.B (A0)+,vDACwAddReg-vDACwDataReg(A3) ; write the index value to the CLUT address r MOVE.B (A0)+,(A3) ; write red MOVE.B (A0)+,(A3) ; write green MOVE.B (A0)+,(A3) ; write blue DBRA D3,IndexWrite ; and loop BRA.S SEDone ; ; write the translated starting position for sequence mode SeqWrite MOVE.W csStart(A2),D0 ; get sequence start address ADD.B D5,D0 ; offset for this mode (never greater then 255!) MOVE.B D0,vDACwAddReg-vDACwDataReg(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) ; get red MOVE.B (A0)+,(A3) ; get green MOVE.B (A0)+,(A3) ; write blue DBRA D3,SeqLoop ; and loop ; ; clean up and go home ; SEDone MOVE (SP)+,SR ; restore status register ADD D4,SP ; release stack buffer MOVEM.L (SP)+,A1/A4-A6/D4-D7 ; restore registers BRA CtlGood ; return O-Tay! BadExit MOVEM.L (SP)+,A1/A4-A6/D4-D7 ; restore registers BRA CtlBad ; return an error code SetGamma ;--------------------------------------------------------------------- ; ; 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 DCE ; A2 = Ptr to cs parameter record ; ;--------------------------------------------------------------------- ; 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 CtlBad ; => no, return error TST.W GType(A2) ; test the hardware ID BEQ.S ChangeTable ; if 0, then accept a TFB gamma table CMP.W #drHwRBV,GType(A2) ; type = Aurora? BNE CtlBad ; => no, return error TST.W gFormulaSize(A2) ; if gType=Aurora, 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 saveID(A3),D0 ; is this the monitor? BEQ.S ChangeTable ; yes, so do it ADDQ #1,D0 ; was it -1? BNE CtlBad ; nope, so must be wrong monitor ; if new table is 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 _DisposPtr ; if new one smaller, dispose old one CLR.L saveGammaPtr(A3) ; flag it's been disposed @GetNew 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 CtlBad ; => unable to allocate storage MOVE.L saveGammaPtr(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 ; 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 CtlGood ; => return no error ; ; 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 A2 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 ADDA #GFormulaData,A0 ; point to tables ADDA D0,A0 ; point past monID, if present @ChanLoop MOVE.W #255,D0 ; loop count within each channel @entryLoop MOVE.B D0,(A0) ; write value NOT.B (A0)+ ; invert it to make table ramp properly DBRA D0,@entryLoop ; for each entry in channel DBRA D2,@ChanLoop ; and each channel BRA CtlGood ; all done GrayPage ;--------------------------------------------------------------------- ; ; Clear the specified page in the current mode to gray ; ; A0 = Ptr to private storage ; A1 = Ptr to DCE ; A2 = Ptr to cs parameter record ; ;--------------------------------------------------------------------- MOVE saveMode(A3),D1 ; D1 = mode MOVE D1,csMode(A2) ; force current mode, just in case for ChkPage BSR ChkMode ; convert mode to depth in D1 BNE CtlBad ; => not a valid depth MOVE csPage(A2),D0 ; D0 = page BNE CtlBad ; => not a valid page BSR GrayScreen ; paint the screen gray BRA CtlGood ; => return no error SetGray ;--------------------------------------------------------------------- ; ; 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 DCE ; A2 = Ptr to cs parameter record ; ;--------------------------------------------------------------------- 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 SetIntCom ; call common code BRA CtlGood ; 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 ; SetIntCom MOVE.B csMode(A2),D0 ; get boolean BFINS D0,GFlags(A3){D1:1} ; set flag bit RTS ; and return SetInterrupt ;--------------------------------------------------------------------- ; ; 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 DCE ; A2 = Ptr to cs parameter record ; A3 = Ptr to private storage ; ;--------------------------------------------------------------------- WITH VDPageInfo,SlotIntQElement MOVEQ #1,D1 ; set up for BFEXTU to point to IntDisFlag BSR.S SetIntCom ; call common code BNE.S DisableThem ; if zero, then enable ; ; this code enables interrupts and installs the interrupt handler ; BSR.S EnableVGuts ; call common code BNE CtlBad ; error, flag problem BRA CtlGood ; and go home ; ; this code disables VBL interrupts, then removes the interrupt handler ; DisableThem BSR.S DisableVGuts ; jump to the disabling utility BRA CtlGood ; all done ; ; the following two routines are common code shared between the Open/Close calls ; and the SetInterrupt control call ; DisableVGuts BSR WaitVSync ; to be safe, wait for the next VBL MOVE.L RBV,A0 ; point to the RBV base MOVE.B #$40,RvSEnb(A0) ; set slot 0 interrupt disabled (slot 0 bit+set/clear to 0) <1.3> CLR D0 ; setup slot # for _SIntRemove (slot zero!) MOVE.L saveSQElPtr(A3),A0 ; get the SQ element pointer _SIntRemove ; remove the interrupt handler RTS EnableVGuts ; MOVE.L saveSQElPtr(A3),A0 ; get the queue element LEA BeginIH,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 dCtlStorage(A1),A2 ; base handle to privates MOVE.L (A2),A2 ; pointer to privates (this must stay locked!!) MOVE.L A2,SQParm(A0) ; pass this as the parameter CLR.W D0 ; setup slot zero _SIntInstall ; and do install BNE.S IntBad MOVE.L A0,-(SP) ; save this for a second MOVE.L RBV,A0 ; point to the RBV base MOVE.B #$C0,RvSEnb(A0) ; set slot 0 interrupt enabled (slot 0 bit+set/clear to 1) <1.3> MOVE.L (SP)+,A0 MOVE D0,D0 ; clear z-bit for good result RTS ; return home ; ; in the event there is a problem, return Z-flag off ; IntBad MOVEQ #1,D0 ; clear Z bit RTS ENDWITH SetDefaultMode ;--------------------------------------------------------------------- ; ; Write the card default mode into slot pRAM. ; ; A1 = Ptr to DCE ; 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 ; ; 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. Aurora keeps the video sRsrc ; spID in VendorUse2. This guy DOESN'T check to make sure that the mode being set if ; valid for the display, but that's OK since Monitors can't see the other modes anyway ; SUBA #SizesPRAMRec,SP ; allocate block for pRAM record MOVE.L SP,spResult(A0) ; point to it _sReadPRAMRec ; read it ; ; The parameter list id (identifying the screen depth) in 2(SP) will still be valid. ; ; It is very important that Monitors (or someone) invalidate and setup the screen resource ; if this call is exercised. Monitors needs to verify (and potentially re-write to pRAM) ; the proper screen depth in the new world. ; MOVE.B csMode(A2),3(SP) ; write the mode into pRAM buffer MOVE.L SP,spsPointer(A0) ; set up parameter block _SPutPRAMRec ; write the new record out ADDA #SizesPRAMRec+spBlockSize,SP ; deallocate buffer BRA CtlGood ********************************************************************** * * VideoClose releases the device's private storage and removes the * interrupt handler. * * * Entry: A0 = param block pointer * A1 = DCE pointer * * Other: A2 = temporary DCE pointer copy * ********************************************************************** VideoClose MOVE.L dCtlStorage(A1),A3 MOVE.L (A3),A3 ; get pointer to private storage BSR DisableVGuts ; call utility to deactivate interrupts LEA TTask(A3),A0 ; get the time manager task block _RmvTime ; remove this element from the queue MOVE.L saveSQElPtr(A3),A0 ; get the slot interrupt queue element ptr _DisposPtr MOVE.L saveGamDispPtr(A3),A0 ; get pointer to gamma table _DisposPtr ; and dispose it MOVE.L dCtlStorage(A1),A0 ; Dispose of the private storage _DisposHandle ; MOVEQ #0,D0 ; no error RTS ; and return ********************************************************************** * * Video Driver Status Call Handler. Right now there are nine calls: * * (0) Error * (1) Error * (2) GetMode * (3) GetEntries * (4) GetPage * (5) GetPageBase * (6) GetGray * (7) GetInterrupt * (8) GetGamma * (9) GetDefaultMode * * Entry: A0 = param block * A1 = DCE pointer * Uses: A2 = cs parameters * A3 = pointer to private storage * D0-D3 = scratch (don't need to be preserved) * * Exit: D0 = error code * ********************************************************************** VideoStatus MOVE.L A0,-(SP) ; save a register MOVE.W csCode(A0),D0 ; get the opCode MOVE.L csParam(A0),A2 ; A2 <- Ptr to control parameters MOVE.L dCtlStorage(A1),A3 MOVE.L (A3),A3 ; get pointer to private storage CMP.W #9,D0 ;IF csCode NOT IN [0..9] THEN BHI.S StatBad ; Error, csCode out of bounds. LSL.W #1,D0 ;Adjust csCode to be an index into the table. MOVE.W StatJumpTbl(PC,D0.W),D0 ;Get the relative offset to the routine. JMP StatJumpTbl(PC,D0.W) ;GOTO the proper routine. StatJumpTbl DC.W StatBad-StatJumpTbl ;$00 => Error DC.W StatBad-StatJumpTbl ;$01 => Error DC.W GetMode-StatJumpTbl ;$02 => GetMode DC.W GetEntries-StatJumpTbl ;$03 => GetEntries DC.W GetPage-StatJumpTbl ;$04 => GetPage DC.W GetPageBase-StatJumpTbl ;$05 => GetPageBase DC.W GetGray-StatJumpTbl ;$06 => GetGray DC.W GetInterrupt-StatJumpTbl ;$07 => GetInterrupt DC.W GetGamma-StatJumpTbl ;$08 => GetGamma DC.W GetDefaultMode-StatJumpTbl ;$09 => GetDefaultMode StatBad MOVEQ #statusErr,D0 ; else say we don't do this one BRA.S StatDone ; and return StatGood MOVEQ #noErr,D0 ; return no error StatDone MOVE.L (SP)+,A0 ; restore registers. BRA ExitDrvr GetMode ;--------------------------------------------------------------------- ; ; Return the current mode ; ; Inputs : A2 = pointer to csParams ; A3 = pointer to private storage ; ;--------------------------------------------------------------------- MOVE.W saveMode(A3),csMode(A2) ; return the mode CLR.W csPage(A2) ; return the page number MOVE.L saveBaseAddr(A3),csBaseAddr(A2) ; and the base address BRA.S StatGood ; => return no error GetEntries ;--------------------------------------------------------------------- ; ; Read the current contents of the CLUT. These values were gamma corrected ; when they were set (by SetEntries), so they may not match the source ; cSpec array. ; ; Inputs : A2 = pointer to csParams ; ;--------------------------------------------------------------------- MOVE.L csTable(A2),D0 ; Check for a nil pointer BEQ.S StatBad MOVEM.L A1/A3/A4/D4/D1,-(SP) ; save work registers MOVE.L D0,A4 ; A4 <- pointer to table ; calculate the index range in D3 MOVE.W saveMode(A3),D1 ; get the current video mode SUB.W #FirstVidMode,D1 ; convert to index LEA CountTbl,A0 ; point to little table of counts MOVE.B (A0,D1),D3 ; pick the zero-based byte LEA StartTbl,A0 ; point to table of starting positions MOVE.B (A0,D1),D4 ; pick up start offset position MOVE.W csCount(A2),D0 ; get the count MOVE.W D0,D2 ; make a copy of the count CMP.W #-1,csStart(A2) ; is it index or sequence mode? BEQ.S GECom ; if index, then continue MOVE.W D0,D1 ; copy count into another loop counter ADD.W csStart(A2),D2 ; get last index @1 MOVE.W D2,value(A4,D1*8) ; write the index into the table SUBQ #1,D2 ; decrease index DBRA D1,@1 ; for all indices GECom MOVE.L saveVDACBase(A3),A1 ; get base address of CLUT ADDQ #vDACwDataReg,A1 ; read and write data are the same register MOVE.W SR,-(SP) ; preserve the status register MOVEQ #7,D1 ; get mask in D0 AND.B (SP),D1 ; get the interrupt level SUBQ.B #2,D1 ; 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 @Repeat MOVE.W value(A4),D1 ; get the next position in D1 CMP.B D3,D1 ; Is this request in range? BHI.S @Until ; if hi, then no, the request is invalid ADD.B D4,D1 ; offset it's position for pixel depth MOVE.B D1,vDACrAddReg-vDACwDataReg(A1) ; set the CLUT to read from this addr MOVE.B (A1),D1 ; get red MOVE.B D1,rgb+red(A4) ; byte->word in the dest cSpecArray MOVE.B D1,rgb+red+1(A4) ; MOVE.B (A1),D1 ; get green MOVE.B D1,rgb+green(A4) ; MOVE.B D1,rgb+green+1(A4) ; MOVE.B (A1),D1 ; get blue MOVE.B D1,rgb+blue(A4) ; MOVE.B D1,rgb+blue+1(A4) ; @Until ADDQ.L #colorSpecSize,A4 ; advance A4 to the next color table element DBRA D0,@Repeat ; and loop until done MOVE (SP)+,SR ; restore the status register MOVEM.L (SP)+,A1/A3/A4/D4/D1 ; restore work register BRA StatGood ; => return no error GetPage ;--------------------------------------------------------------------- ; ; Return the number of pages in the specified mode. Cobra always has ; one video page, so this is pretty simple. ; ;--------------------------------------------------------------------- MOVE csMode(A2),D1 ; get the mode MOVE D1,D2 ; keep a copy BSR.S ChkMode ; is this mode OK? BGT StatBad ; => not a valid mode MOVE.W #1,csPage(A2) ; return page count BRA StatGood ; => return no error GetPageBase ;--------------------------------------------------------------------- ; ; Return the base address for the specified page in the current mode ; ;--------------------------------------------------------------------- MOVE saveMode(A3),D1 ; get the current mode MOVE D1,csMode(A2) ; force current mode, just in case for ChkPage BSR.S ChkMode ; convert to depth in D1 ; (assume current mode ok - we set it, after all) MOVE.W csPage(A2),D0 ; get the requested page CMP #0,D0 ; only page 0 is legal BNE StatBad ; => no, just return MOVE.L saveBaseAddr(A3),csBaseAddr(A2) ; return the base address BRA StatGood ; => return no error GetGray ;--------------------------------------------------------------------- ; ; Return a boolean, set true if luminance mapping is on ; ;--------------------------------------------------------------------- MOVEQ #0,D1 ; set up for BFEXTU GetFlagCom BFEXTU GFlags(A3){D1:1},D0 ; get the state of flag MOVE.B D0,csMode(A2) ; return value BRA StatGood ; => and return GetInterrupt ;--------------------------------------------------------------------- ; ; Return a boolean in csMode, set true if VBL interrupts are disabled ; ;--------------------------------------------------------------------- MOVEQ #1,D1 ; set up BFEXTU to point at IntDisFlag BRA.S GetFlagCom ; and use common code GetGamma ;--------------------------------------------------------------------- ; ; Return the pointer to the current gamma table ; ;--------------------------------------------------------------------- MOVE.L saveGammaPtr(A3),csGTable(A2) ; return the pointer to the structure BRA StatGood ; and return a good result GetDefaultMode ;--------------------------------------------------------------------- ; ; Read the card default mode from slot pRAM. ; ; A1 = Ptr to DCE ; A2 = Ptr to cs parameter record ; A3 = Ptr to private storage ; ;--------------------------------------------------------------------- WITH spBlock ; ; 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. Aurora 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 3(SP),csMode(A2) ; return the result ADDA #SizesPRAMRec+spBlockSize,SP ; release buffer BRA StatGood ; ENDWITH ;--------------------------------------------------------------------- ; ; Exit from control or Status. ; ;--------------------------------------------------------------------- ExitDrvr BTST #NoQueueBit,ioTrap(A0) ; no queue bit set? BEQ.S GoIODone ; => no, not immediate RTS ; otherwise, it was an immediate call GoIODone MOVE.L JIODone,A0 ; get the IODone address JMP (A0) ; invoke it ;===================================================================== ; ; Utilities ; ;===================================================================== ;--------------------------------------------------------------------- ; ; 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. ; ChkMode MOVEM.L D0/D2,-(SP) ; save regs SUB.B #FirstVidMode,D1 ; make it zero based MOVE #FourthVidMode-FirstVidMode,D2 ; the greatest possible screen depth's number BigTest MOVE.W saveID(A3),D0 ; get the monitor type CMP #SEId,D0 ; is it the SE display? BEQ.S UpTo3 ; CMP #GSId,D0 ; is it the GS display? BEQ.S UpTo3 ; CMP #HRId,D0 ; is it the Mac II display? BEQ.S Speed ; if so then adjust for speed ; must be a portrait display, which doesn't have 8-bit/pixel SUBQ #1,D2 ; Speed BTST #Is16,GFlags(A3) ; if this a 16MHz processor BEQ.S UpTo3 SUBQ #1,D2 ; 16MHz HR and FP have one less mode UpTo3 CMP D2,D1 ; is it too big? BGT.S ModeBad ; yes, so return ModeOK CMP.W D1,D1 ; get EQ ModeBad MOVEM.L (SP)+,D0/D2 ; restore trashed regs RTS ; EQ if valid depth ;--------------------------------------------------------------------- ; ; Wait for vertical blanking. ; ; A1 = DCE POINTER ;--------------------------------------------------------------------- WaitVSync MOVEM.L A0/D0,-(SP) ; save work registers MOVE.W SR,-(SP) ; preserve the status register <2> MOVEQ #7,D0 ; get mask in D0 <2> AND.B (SP),D0 ; get the interrupt level <2> SUBQ.B #2,D0 ; <2> BGE.S @OK ; if ³, then don't change <2> ORI.W #$0200,SR ; raise above level-2 <2> ANDI.W #$FAFF,SR ; make it level-2 <2> @OK MOVE.L RBV,A0 ; point to RBV ADDA #RvSInt,A0 ; point to interrupt register @0 MOVE.B (A0),D0 ; Read the Vert-Sync state, BTST #6,D0 ; and test it. BEQ.S @0 ; Repeat until it goes high ; @1 MOVE.B (A0),D0 ; Read the Vert-Sync state, BTST #6,D0 ; and test it. BNE.S @1 ; Repeat until it goes low MOVE (SP)+,SR ; re-enable cursor interrupts MOVEM.L (SP)+,A0/D0 ; restore work registers RTS RBVSetDepth ;--------------------------------------------------------------------- ; SetDepth sets the RBV depth, and returns the frame buffer base ; in driver privates ; D1 contains the spID of the depth ; A2 = parameter block pointer ; A3 = dCtlStorage pointer ; Preserves all registers ;--------------------------------------------------------------------- MOVEM.L D0/A0/A1,-(SP) ; save regs we are using <1.8> MOVE.L RBV,A0 ; point to RBV base ADDA #RvMonP,A0 ; get the monitor control register MOVE.B D1,(A0) ; set the mode MOVE.W csMode(A2),saveMode(A3) ; save mode number MOVEA.l UnivInfoPtr,a1 ; point to the ProductInfo record <1.8> ADDA.l ProductInfo.VideoInfoPtr(a1),a1 ; point to the VideoInfo record <1.8> MOVE.L VideoInfo.VRAMLogAddr32(a1),saveBaseAddr(A3) ; save base address too <1.8> ADD.L #defmBaseOffset,saveBaseAddr(A3) ; for correctness (it's zero!) MOVEM.L (SP)+,D0/A0/A1 ; restore all regs <1.8> RTS ; return GrayScreen ;--------------------------------------------------------------------- ; ; 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. ; A3 = driver private storage ; ; All registers are preserved ; MOVEM.L D0-D3/A0-A1,-(SP) ; save all registers MOVE.W saveID(A3),D0 ; get the current monitor type LEA Pats,A1 ; get a pointer to the pattern table MOVE.L (A1,D1*4),D3 ; D2 = the proper pattern CMP #GSId,D0 ; is it mod'd GS display BNE.S @1 ; LEA GSParms,A1 ; BRA.S Blast2 ; @1 CMP #HRId,D0 ; is it Mac II display? BNE.S @2 ; LEA HRParms,A1 ; BRA.S Blast2 ; @2 CMP #SEId,D0 ; is it Mac SE display? BNE.S @3 ; LEA SEParms,A1 ; BRA.S Blast2 ; ; must be a portrait display (color or monochrome) if we got here @3 LEA FPParms,A1 ; Blast2 MOVE.L saveBaseAddr(A3),A0 ; get the frame buffer base address MOVE.W (A1),D0 ; get the # of rows @NxtRow MOVE.W 2(A1,D1*2),D2 ; get the # of longs/row @NxtLong MOVE.L D3,(A0)+ ; write gray DBRA D2,@NxtLong ; for each scanline NOT.L D3 ; invert pattern on next row DBRA D0,@NxtRow ; for each row MOVEM.L (SP)+,D0-D3/A0-A1 ; restore registers RTS Pats DC.L OneBitGray,TwoBitGray,FourBitGray,EightBitGray ; ; some simple screen size parameters. The first number is the number of scanlines to ; erase for each mode. The next four numbers are the (decremented for DBRA) rowlongs ; per scanline values for each depth. ; FPParms DC.W defmBounds_BFP-1,(OBMFPRB/4)-1,(TBMFPRB/4)-1,(FBMFPRB/4)-1 GSParms DC.W defmBounds_BGS-1,(OBMGSRB/4)-1,(TBMGSRB/4)-1,(FBMGSRB/4)-1,(EBMGSRB/4)-1 HRParms DC.W defmBounds_BHR-1,(OBMHRRB/4)-1,(TBMHRRB/4)-1,(FBMHRRB/4)-1,(EBMHRRB/4)-1 SEParms DC.W defmBounds_BSE-1,(OBMSERB/4)-1,(TBMSERB/4)-1,(FBMSERB/4)-1,(EBMSERB/4)-1 ;------------------------------------------------------------- ; The Interrupt handler for Cobra board ;------------------------------------------------------------- ; The interrupt handler for the Cobra internal video board. ; ; There's a fairly serious bug in the RBV design. The interrupt line for slot zero is not ; clearable in software. Rather, it clears itself automatically. Unfortunately, the time ; interval that this signal is presented is quite long - the entire duration of the vertical ; sync pulse (3 scanlines!). The slot interrupt handler is level (rather than edge) sensitive, ; therefore, this interrupt handling routine is repeatedly called during the vBlank period. ; To correct this problem, this interrupt handler calls the slot interrupt task queue, then ; tests to see if the interrupt state is still active. If it is, then the handler masks the ; interrupt from slot zero, and sets a Time manager event to re-enable the interrupts after ; two milleseconds. By this time, we are back in active video, so the re-calling problem is ; taken care of. ; ; On entry A1 contains the pointer to the driver's private storage ; D0-D3/A0-A3 have been preserved. BeginIH MOVE.L A1,-(SP) ; save pointer to privates CLR.W D0 ; set slot zero in D0 MOVE.L JVBLTask,A0 ; call the VBL task manager JSR (A0) ; with slot # in D0 MOVE.L (SP)+,A1 ; restore A1 MOVE.L RBV,A0 ; get the RBV base address BTST #6,RvSInt(A0) ; test the slot zero interrupt state BNE.S @Done ; if clear, then video is out of the sync period. MOVE.B #$40,RvSEnb(A0) ; set slot 0 interrupt disabled (slot 0 bit+set/clear to 0) <1.3> LEA TTask(A1),A0 ; get the time manager task pointer MOVEQ #2,D0 ; delay for 2 milleseconds MOVE.L jPrimeTime,A1 ; point straight at the Time Manager dispatch vector JSR (A1) ; start the delay going @Done MOVEQ #1,D0 ; signal that int was serviced RTS ; and return to caller ; ; This is the time manager task. It was only posted if the VBL task queue for the internal video ; finished before the end of vSync. In this case, the slot 0 interrupts have been disabled. ; This routine turns the interrupts back on. ; TimeHandler MOVE.L RBV,A0 ; MOVE.B #$C0,RvSEnb(a0) ; re-enable the slot interrupt RTS END