; ; File: V8Driver.a ; ; Contains: This file contains the video driver for use by the Macintosh ; OS for the Elsie/V8 hardware. ; ; Written by: David Fung/Mike Puckett ; ; Copyright: © 1990-1993 by Apple Computer, Inc. All rights reserved. ; ; Change History (most recent first): ; ; 01-08-93 jmp Updated the version number from 0.0 (LC/LC II) to 0.1 (LC930). ; 01-07-93 jmp Fixed a problem where plugging in a VGA-sensed would cause the ; death-chimes to play (had to do with the fact that we donÕt ; support gamma tables on VGA displays). ; 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,H6) A couple of labels in the GetEntries code we exiting ; thru StatGood/StatBad instead of their respective V8 ; counterparts. ; (jmp,H5) Corrected .s vs. non-.s branches and odd-alignment ; problems. ; 6/4/92 KW (jmp,H4) Eliminated support for the no-vRAM case. ; (jmp,H3) Changed the way the base address is gotten and saved. ; (Use the dCtlDevBase instead of VideoInfo due to the differences ; between the video base with and without vRAM installed.) ; (jmp,H1) Roll-in LC II changes. In routine "V8SetEntries" we ; should have been exiting through V8SEDone instead of SEDone. ; ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ ; Pre-SM ROM comments begin here. ; ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ ; <2> 3/31/92 JSM Rolled this file into Reality. ; <1> 12/8/90 HJR First time for Terror. ; <8> 9/17/90 DAF Integrated code review changes. Relocated fake prime installing ; routine to Open call, fixed 16-bit gray pattern for alpha, ; corrected error code ordering in Open, optimized graying loops ; in two places, improved status flag setting in V8DisableGuts ; <7> 9/5/90 JJ Rex V8: Fix bug in VDAC equates used in GetEntries. ; <6> 9/5/90 JJ Fixed screen graying bug in non-vRAM HiRes mode ; <5> 9/5/90 JJ Corrected bug in screen graying utility (incorrect offset ; between 256K and 512K tables). ; <4> 8/30/90 DAF Fix Apple // mode setting in V8SetDepth ; <3> 8/16/90 JJ Added GetEntries, updated various routines for all vRAM ; configurations, updated equate names / DAF ; <2> 8/8/90 JJ Fixed CLUT graying in direct (16-bit) mode ; <1> 8/7/90 JJ Added Elsie/V8 video driver 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 '_sV8Driver' BLANKS ON STRING ASIS MACHINE MC68020 ; This is device storage which is stored in the dCtlStorage field of the DCE. V8VidPrivates 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 saveVDACBase DS.L 1 ; the base addr of the VDAC GFlags DS.W 1 ; flags word saveMode DS.W 1 ; the current mode setting saveID DS.W 1 ; monitor type ID saveSlotId DS.W 1 ; spID of video sRsrc (hi-order byte only!) V8VidPrivSize EQU * ENDR LV8Driver MAIN EXPORT ;------------------------------------------------------------------- ; Video Driver Header ;------------------------------------------------------------------- V8Drvr DC.W $4C00 ; ctl,status,needsLock DC.W 0,0,0 ; not an ornament ; Entry point offset table DC.W V8VidOpen-V8Drvr ; open routine DC.W V8Drvr-V8Drvr ; no prime in normal video drivers DC.W V8VidCtl-V8Drvr ; control DC.W V8VidStatus-V8Drvr ; status DC.W V8VidClose-V8Drvr ; close STRING Pascal V8VidTitle DC.B '.Display_Video_Apple_V8' ALIGN 2 ; make sure we're aligned DC.W CurV8DrvrVersion ; current version STRING ASIS ; ; V8CLUTTbl 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. Unlike the Bt478 vDAC in the ci and Erickson, ; the Ariel only has adjacent active positions in eight-bit mode. In the lesser depths, the active ; entries are distributed throughout the CLUT address space. As a result, we use sequential CLUT mode ; ONLY in eight-bit mode! 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) V8CLUTTbl 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 CLUTDatRec RECORD 0 ; Range DS.B 1 ; maximum index value in this depth Start DS.B 1 ; lowest active CLUT address Skip DS.W 1 ; skip value between active addresses ; (it's a .W to simplify multiplies!) ENDR ********************************************************************** * * V8VidOpen 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,V8VidPrivates V8VidOpen ; ; Allocate private storage (since block is CLEAR, GFlags are zeroed) and get ; a pointer to it in A3 ; MOVEQ #V8VidPrivSize,D0 ; get size of parameters _ResrvMem ,SYS ; make room as low as possible MOVEQ #V8VidPrivSize,D0 ; get size of parameters _NewHandle ,SYS,CLEAR ; get some memory for private storage BNE @OpErrorC ; => 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. ; WITH ProductInfo,DecoderInfo,VideoInfo 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 ; ; remember the frame buffer base as well. The Open call doesn't set the video mode ; or page, but SetMode will based on this value ; MOVE.L dCtlDevBase(A1),saveBaseAddr(A3) ; save the screen base address MOVE.B dCtlSlotId(A1),saveSlotId(A3) ; save video sRsrc ID ; ; 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 @OpErrorB ; if not allocated, return bad MOVE.L A0,saveSQElPtr(A3) ; save the SQ element pointer. BSR V8EnableVGuts ; do it BNE @OpErrorB ; ; ; read the V8 to find out what kind of monitor we have. To have gotten here, we MUST ; have a valid video device. ; MOVE.L V8,A2 ; get the V8 base address MOVE.B V8MonP(A2),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 ; ; 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 @DoLinear ; if failed, then do linear 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 @DoLinear ; if failed, then do linear ; ; skip over 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.L #0,D0 ; round it off MOVE.L D0,saveGammaPtr(A3) ; put it in private storage ; ; 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 @OpErrorA ; 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 #drHwElsie,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. ADDA #spBlockSize,SP ; release the parameter block ; ; all done! ; @AllDone MOVEQ #0,D0 ; no error @EndOpen RTS ; return @OpErrorA ADDA #spBlockSize,SP ; release the spBlock @OpErrorB MOVE.L dCtlStorage(A1),A0 ; get the private storage back _DisposHandle ; release the driver private storage @OpErrorC MOVE.L #OpenErr,D0 ; say can't open driver 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 ; V8VidCtl MOVE.L A0,-(SP) ; save work registers (A0 is saved because it is used by ExitDrvr) MOVE.W csCode(A0),D0 ; get the opCode CMP.W #128,D0 ; is it the restart graying call (id=128?) BNE.S @0 BRA V8Goodbye ; do the graying call @0 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 V8CtlBad ; Error, csCode out of bounds MOVE.W V8CtlJumpTbl(PC,D0.W*2),D0 ; Get the relative offset to the routine JMP V8CtlJumpTbl(PC,D0.W) ; GOTO the proper routine V8CtlJumpTbl DC.W V8VidReset-V8CtlJumpTbl ; $00 => VidReset DC.W V8CtlGood-V8CtlJumpTbl ; $01 => CtlGood (no async routines here) DC.W V8SetVidMode-V8CtlJumpTbl ; $02 => SetVidMode DC.W V8SetEntries-V8CtlJumpTbl ; $03 => SetEntries DC.W V8SetGamma-V8CtlJumpTbl ; $04 => SetGamma DC.W V8GrayPage-V8CtlJumpTbl ; $05 => GrayPage DC.W V8SetGray-V8CtlJumpTbl ; $06 => SetGray DC.W V8SetInterrupt-V8CtlJumpTbl ; $07 => SetInterrupt DC.W V8DirectSetEntries-V8CtlJumpTbl ; $08 => DirectSetEntries DC.W V8SetDefaultMode-V8CtlJumpTbl ; $09 => SetDefaultMode V8CtlBad MOVEQ #controlErr,D0 ; else say we don't do this one BRA.S V8CtlDone ; and return V8CtlGood MOVEQ #noErr,D0 ; return no error V8CtlDone MOVE.L (SP)+,A0 ; restore registers. BRA V8ExitDrvr V8VidReset ;--------------------------------------------------------------------- ; ; Reset the card to its default ; ;--------------------------------------------------------------------- WITH V8VidPrivates 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 V8SetDepth ; set the depth from D1 MOVE.L saveBaseAddr(A3),csBaseAddr(A2) ; return the base address BSR V8GrayScreen ; paint the screen gray BRA.S V8CtlGood ; => no error ENDWITH V8SetVidMode ;--------------------------------------------------------------------- ; ; 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 V8VidPrivates MOVE.W csMode(A2),D1 ; D1 = mode BSR V8ChkMode ; check mode and convert BNE.S V8CtlBad ; => not a valid mode TST.W csPage(A2) ; only page zero is valid BNE.S V8CtlBad ; => not a valid page ; Only set if mode has changed MOVE.W csMode(A2),D2 ; get the mode spID (D1 has the zero-based mode) CMP saveMode(A3),D2 ; has the mode changed? BEQ.S @ModeOK1 ; if not, then skip graying get out ; 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 ; raise to interrupt level 2 the new, good way MOVE.W SR,-(SP) ; preserve the status register MOVEQ #7,D2 ; get mask in D0 AND.B (SP),D2 ; get the interrupt level SUBQ.B #2,D2 ; 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 BSR V8WaitVSync ; wait for next blanking period ; we don't need to test the entire mode here, just blank everything MOVE.L saveVDACBase(A3),A0 ; get the VDAC base addr ADDA #V8DACwDataReg,A0 ; point to data register CLR.B V8DACwAddReg-V8DACwDataReg(A0) ; start at the beginning of CLUT, 4-bit mode MOVE #(256*3)-1,D2 ; get count <8> @Repeat MOVE.B D3,(A0) ; put component (red=green=blue) <8> DBRA D2,@Repeat ; MOVE (SP)+,SR ; restore the status reg BSR V8SetDepth ; set the depth from D1, page from D0 @ModeOK1 MOVE.L saveBaseAddr(A3),csBaseAddr(A2) ; return the base address BRA V8CtlGood ; => return no error ENDWITH V8SetEntries ;--------------------------------------------------------------------- ; ; 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 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 V8VidPrivates BTST #IsDirect,GFlags(A3) ; are we in a direct mode? BNE V8CtlBad ; error if so SEGuts TST.L csTable(A2) ; Check for a nil pointer BEQ V8CtlBad MOVEM.L A1/A4-A6/D4-D7,-(SP) ; save registers for gamma MOVE.W GFlags(A3),D5 ; get GFlags word in D5 (sequence bits are always cleared here) CMP.W #-1,csStart(A2) ; is it sequence mode? BEQ.S @5 ; nope, it's index BSET #PsuedoIndex,D5 ; turn on the bit that denotes a seq write that was xlated to indexed CMP.W #FourthVidMode,saveMode(A3) ; is it 8- or 16-bit mode? BLT.S @5 ; if not, need to use indexed mode BSET #UseSeq,D5 ; if sequence then set bit @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 ASL #1,D0 ; mulu times 2 ADDA D0,A6 ; calc base of blue @OneTbl ; save the VDAC base address for later. We're going to torch the privates reg (A3) now MOVE.L saveVDACBase(A3),D6 ; save VDAC base from driver privates ; 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 WITH CLUTDatRec LEA (V8CLUTTbl,PC,D1*4),A3 ; point to table of CLUT data ; ; 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 Range(A3),D3 ; is it in the allowable range? BHI V8BadExit ; 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 ; 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 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 BTST #PsuedoIndex,D5 ; was this an indexed request, or is it a non-8bit sequenc BEQ.S @IndexPresent ; ; 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 sequence counter to D0 ADDQ.W #1,D4 ; increment sequence counter @IndexPresent MULU.W Skip(A3),D0 ; calc the new position at this depth ADD.B Start(A3),D0 ; add the first entry 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 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},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 MOVE.B (A4,D0),(A0)+ ; write gamma corrected red MOVE.B (A5,D1),(A0)+ ; write gamma corrected green MOVE.B (A6,D2),(A0)+ ; write gamma corrected blue DBRA D3,@SetupLoop ; and loop for each entry ENDWITH 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.W csCount(A2),D3 ; get the count again MOVE.L D6,A3 ; get VDAC base that we pushed above LEA V8DACwDataReg(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 V8WaitVSync ; 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 the loop that actually writes to the hardware when in indexed mode ; @IndexWrite MOVE.B (A0)+,V8DACwAddReg-V8DACwDataReg(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,@IndexWrite ; and loop BRA.S V8SEDone ;

; write the translated starting position for sequence mode @SeqWrite MOVE.W csStart(A2),D0 ; get sequence start address (exact since it has to be 8-bit mode) MOVE.B D0,V8DACwAddReg-V8DACwDataReg(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 ; V8SEDone MOVE (SP)+,SR ; restore status register ADD D4,SP ; release stack buffer MOVEM.L (SP)+,A1/A4-A6/D4-D7 ; restore registers BRA V8CtlGood ; return O-Tay! V8BadExit ADDA #4,SP ; kill pushed vDAC address MOVEM.L (SP)+,A1/A4-A6/D4-D7 ; restore registers BRA V8CtlBad ; return an error code ENDWITH V8SetGamma ;--------------------------------------------------------------------- ; ; 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 ; ;--------------------------------------------------------------------- WITH V8VidPrivates ; get new gamma table and check that we know how to handle it MOVE.L csGTable(A2),D0 ; test for a NIL pointer BEQ V8LinearTab ; 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 V8CtlBad ; => no, return error TST.W GType(A2) ; test the hardware ID BEQ.S V8ChangeTable ; if 0, then accept a TFB gamma table CMP.W #drHwElsie,GType(A2) ; type = Elsie? BNE V8CtlBad ; => no, return error TST.W gFormulaSize(A2) ; if gType=Aurora, then check for monID in gFormulaData BEQ.S V8ChangeTable ; 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 V8ChangeTable ; yes, so do it ADDQ #1,D0 ; was it -1? BNE V8CtlBad ; nope, so must be wrong monitor ; if new table is different size, reallocate memory V8ChangeTable 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 V8CtlBad ; => 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 BTST #IsDirect,GFlags(A3) ; are we in a direct mode? BEQ.S @Out ; if not, then we're done BSR V8DirectCLUTSet ; is so, then set up direct CLUT ramps @Out BRA V8CtlGood ; => return no error ; ; set up a linear gamma table. To prevent memory thrash, build this new one <7> ; the same size as the existing one (one or three channel). ; V8LinearTab 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 #$FF,D0 ; loop count within each channel @entryLoop MOVE.B D0,(A0,D0) ; write this value out DBRA D0,@entryLoop ; for each entry in channel DBRA D2,@ChanLoop ; and each channel BRA V8CtlGood ; all done ENDWITH V8GrayPage ;--------------------------------------------------------------------- ; ; 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 ; A3 = Ptr to driver privates ; ;--------------------------------------------------------------------- WITH V8VidPrivates MOVE saveMode(A3),D1 ; D1 = mode MOVE D1,csMode(A2) ; force current mode, just in case for ChkPage BSR V8ChkMode ; convert mode to depth in D1 BNE V8CtlBad ; => not a valid depth MOVE csPage(A2),D0 ; D0 = page BNE V8CtlBad ; => not a valid page BSR V8GrayScreen ; paint the screen gray BTST #IsDirect,GFlags(A3) ; are we in a direct mode? BEQ.S @Out ; if not, then we're done BSR V8DirectCLUTSet ; is so, then set up direct CLUT ramps @Out BRA V8CtlGood ; => return no error ENDWITH V8SetGray ;--------------------------------------------------------------------- ; ; 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 ; ;--------------------------------------------------------------------- WITH V8VidPrivates MOVEQ #0,D1 ; set up for BFEXTU to point to GrayFlag BSR.S V8SetIntCom ; call common code BRA V8CtlGood ; 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 ; V8SetIntCom MOVE.B csMode(A2),D0 ; get boolean BFINS D0,GFlags(A3){D1:1} ; set flag bit RTS ; and return ENDWITH V8SetInterrupt ;--------------------------------------------------------------------- ; ; 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,V8VidPrivates MOVEQ #1,D1 ; set up for BFEXTU to point to IntDisFlag BSR.S V8SetIntCom ; call common code BNE.S V8DisableThem ; if zero, then enable ; ; this code enables interrupts and installs the interrupt handler ; BSR.S V8EnableVGuts ; call common code BNE V8CtlBad ; error, flag problem BRA V8CtlGood ; and go home ; ; this code disables VBL interrupts, then removes the interrupt handler ; V8DisableThem BSR.S V8DisableVGuts ; jump to the disabling utility BRA V8CtlGood ; all done ; ; the following two routines are common code shared between the Open/Close calls ; and the SetInterrupt control call ; V8DisableVGuts MOVE.W SR,-(SP) ; preserve the status register MOVEQ #7,D0 ; get mask in 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 BSR V8WaitVSync ; to be safe, wait for the next VBL MOVE.L V8,A0 ; point to the VISA base MOVE.B #$40,V8SEnb(A0) ; set slot 0 interrupt disabled (slot 0 bit+set/clear to 0) <1.3> 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 V8EnableVGuts ; MOVE.L saveSQElPtr(A3),A0 ; get the queue element LEA V8BeginIH,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 V8,A0 ; point to the RBV base MOVE.B #$C0,V8SEnb(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 @IntBad RTS ; return home (if bad, z-bit is set above, so just leave) <8> ENDWITH V8DirectSetEntries ;--------------------------------------------------------------------- ; ; Change the CLUT in a direct mode. ; ; A1 = Ptr to DCE ; 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 V8CtlBad ; error if not CMP.W #31,csCount(A2) ; only 32 changeable positions in direct mode BGT V8CtlBad ; error if not BRA SEGuts ; jump to SetEntries internals if it's OK V8SetDefaultMode ;--------------------------------------------------------------------- ; ; 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,V8VidPrivates ; ; 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 V8CtlGood ENDWITH V8Goodbye ;--------------------------------------------------------------------- ; ; Special routine to entire CLUT to 50% gray. ; ; Since the VISA can't suppress video sync, we gray out the CLUT on ; restart to hide any mode change artifacts. We could have buried this ; code in the Shutdown.a, but it's better to collect all the device ; specific code here. ; ; No parameters ; ;--------------------------------------------------------------------- ; raise to interrupt level 2 the new, good way MOVE.W SR,-(SP) ; preserve the status register MOVEQ #7,D2 ; get mask in D0 AND.B (SP),D2 ; get the interrupt level SUBQ.B #2,D2 ; 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 ; blank everything WITH ProductInfo,DecoderInfo,VideoInfo 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),A0 ; get chip base address ADDA #V8DACwDataReg,A0 ; point to data register CLR.B V8DACwAddReg-V8DACwDataReg(A0) ; start at the beginning of CLUT, 4-bit mode ENDWITH MOVE.B #$7F,D3 ; get a 50% value MOVE #($256*3)-1,D2 ; get count <8> @Repeat MOVE.B D3,(A0) ; put component (red=green=blue) <8> DBRA D2,@Repeat ; MOVE (SP)+,SR ; restore the status reg BRA V8CtlGood ********************************************************************** * * 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 * ********************************************************************** V8VidClose WITH V8VidPrivates MOVE.L dCtlStorage(A1),A3 MOVE.L (A3),A3 ; get pointer to private storage BSR V8DisableVGuts ; call utility to deactivate interrupts 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 dCtlStorage(A1),A0 ; Dispose of the private storage _DisposHandle ; MOVEQ #0,D0 ; no error RTS ; and return ENDWITH ********************************************************************** * * 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 * ********************************************************************** V8VidStatus 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 V8StatBad ; Error, csCode out of bounds. LSL.W #1,D0 ;Adjust csCode to be an index into the table. MOVE.W V8StatJumpTbl(PC,D0.W),D0 ;Get the relative offset to the routine. JMP V8StatJumpTbl(PC,D0.W) ;GOTO the proper routine. V8StatJumpTbl DC.W V8StatBad-V8StatJumpTbl ;$00 => Error DC.W V8StatBad-V8StatJumpTbl ;$01 => Error DC.W V8GetMode-V8StatJumpTbl ;$02 => GetMode DC.W V8GetEntries-V8StatJumpTbl ;$03 => GetEntries DC.W V8GetPage-V8StatJumpTbl ;$04 => GetPage DC.W V8GetPageBase-V8StatJumpTbl ;$05 => GetPageBase DC.W V8GetGray-V8StatJumpTbl ;$06 => GetGray DC.W V8GetInterrupt-V8StatJumpTbl ;$07 => GetInterrupt DC.W V8GetGamma-V8StatJumpTbl ;$08 => GetGamma DC.W V8GetDefaultMode-V8StatJumpTbl ;$09 => GetDefaultMode V8StatBad MOVEQ #statusErr,D0 ; else say we don't do this one BRA.S V8StatDone ; and return V8StatGood MOVEQ #noErr,D0 ; return no error V8StatDone MOVE.L (SP)+,A0 ; restore registers. BRA V8ExitDrvr V8GetMode ;--------------------------------------------------------------------- ; ; Return the current mode ; ; Inputs : A2 = pointer to csParams ; A3 = pointer to private storage ; ;--------------------------------------------------------------------- WITH V8VidPrivates MOVE.W saveMode(A3),csMode(A2) ; return the mode MOVE.W #0,csPage(A2) ; return the page number (always 0) MOVE.L saveBaseAddr(A3),csBaseAddr(A2) ; and the base address BRA.S V8StatGood ; => return no error ENDWITH V8GetEntries ;--------------------------------------------------------------------- ; ; 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 V8StatBad MOVEM.L A1/A3-A4/D2-D5,-(SP) ; save work registers MOVE.L D0,A4 ; A4 <- pointer to table ; get the index range in D3 WITH CLUTDatRec LEA V8CLUTTbl,A0 ; point to CLUT parameters MOVE.W saveMode(A3),D0 ; get the screen depth ID SUB #FIrstVidMode,D0 ; make it an index MOVE.B Range(A0,D0*4),D3 ; get the maximum CLUT index MOVE.B Start(A0,D0*4),D4 ; get the starting position MOVE.W Skip(A0,D0*4),D5 ; get the inter-entry skip value ENDWITH MOVE.W csCount(A2),D0 ; get the count MOVE.W D0,D2 ; make a copy of the count TST.W csStart(A2) ; is it index or sequence mode? BMI.S V8GECom ; 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 V8GECom MOVE.L saveVDACBase(A3),A1 ; get base address of CLUT ADDQ #v8DACwDataReg,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 (A4)+,D1 ; get the value field for from the next colorspec in D1 CMP.B D3,D1 ; Is this request in range? BHI.S @Until ; if hi, then no, the request is invalid MULU D5,D1 ; multiply index by skip value ADD.B D4,D1 ; add starting offset MOVE.B D1,V8DACrAddReg-V8DACwDataReg(A1) ; set the CLUT to read from this addr <7> MOVEQ #3-1,D6 ; loop counter for # of channels @CLUTLoop MOVE.B (A1),D1 ; get value MOVE.B D1,(A4)+ ; byte->word in the dest cSpecArray MOVE.B D1,(A4)+ ; DBRA D6,@CLUTLoop ; for each channel @Until DBRA D0,@Repeat ; and loop until done MOVE (SP)+,SR ; restore the status register MOVEM.L (SP)+,A1/A3-A4/D2-D5 ; restore work register BRA V8StatGood ; => return no error V8GetPage ;--------------------------------------------------------------------- ; ; 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 V8VidPrivates MOVE csMode(A2),D1 ; get the mode MOVE D1,D2 ; keep a copy BSR.S V8ChkMode ; is this mode OK? BGT V8StatBad ; => not a valid mode MOVE.W #1,csPage(A2) ; return page count BRA V8StatGood ; => return no error ENDWITH V8GetPageBase ;--------------------------------------------------------------------- ; ; Return the base address for the specified page in the current mode ; ;--------------------------------------------------------------------- WITH V8VidPrivates TST.W csPage(A2) ; are we returning page zero info? BNE V8StatBad ; only page 0 is valid MOVE.L saveBaseAddr(A3),csBaseAddr(A2) ; return screen base address BRA V8StatGood ; => return no error ENDWITH V8GetGray ;--------------------------------------------------------------------- ; ; Return a boolean, set true if luminance mapping is on ; ;--------------------------------------------------------------------- WITH V8VidPrivates MOVEQ #0,D1 ; set up for BFEXTU V8GetFlagCom BFEXTU GFlags(A3){D1:1},D0 ; get the state of flag MOVE.B D0,csMode(A2) ; return value BRA V8StatGood ; => and return ENDWITH V8GetInterrupt ;--------------------------------------------------------------------- ; ; Return a boolean in csMode, set true if VBL interrupts are disabled ; ;--------------------------------------------------------------------- WITH V8VidPrivates MOVEQ #1,D1 ; set up BFEXTU to point at IntDisFlag BRA.S V8GetFlagCom ; and use common code ENDWITH V8GetGamma ;--------------------------------------------------------------------- ; ; Return the pointer to the current gamma table ; ;--------------------------------------------------------------------- WITH V8VidPrivates MOVE.L saveGammaPtr(A3),csGTable(A2) ; return the pointer to the structure BRA V8StatGood ; and return a good result ENDWITH V8GetDefaultMode ;--------------------------------------------------------------------- ; ; 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,V8VidPrivates ; ; 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 V8StatGood ; ENDWITH ;--------------------------------------------------------------------- ; ; Exit from control or Status. ; ;--------------------------------------------------------------------- V8ExitDrvr BTST #NoQueueBit,ioTrap(A0) ; no queue bit set? BEQ.S V8GoIODone ; => no, not immediate RTS ; otherwise, it was an immediate call V8GoIODone 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. ; V8ChkMode WITH V8VidPrivates MOVEM.L D0/D2-D3,-(SP) ; save regs SUB.W #FirstVidMode,D1 ; make modeID zero based MOVE.B saveSlotID(A3),D0 ; get the slot manager mode ID BCLR #4,D0 ; if this bit is on, either A// mode or no vRAM BEQ.S @testDepth ; if not, then do simple test ; if we are in A// emulation mode, then warp D0 to make the monitor look like a High-Res rather than a Rubik. ; We do this because the V8 has one less mode available when in A// mode (8-bit only with 512K vRAM, 4-bit ; with 256K vRAM). Note that the A// mode bit was flipped off in a test above! @adjA2Em BSET #2,D0 ; transmogrify A// Rubik into High-Res ; test for the greatest available depth. We'll set this up in D2 and compare to D1 @testDepth MOVE #ThirdVidMode-FirstVidMode,D2 ; everybody at this point can do 4-bit mode ;+++ BTST #2,D0 ; Rubiks have one more possible mode over Hi-Res & VGA MOVE.B D0,D3 ; copy the spID AND.B #5,D3 ; Rubik = 010, HR=110, VGA=011, so we test for ; Rubik by making sure bits 0 and 2 are clear BNE.S @vRAMTest ; if not, then continue ADDQ #1,D2 ; reflect the additional mode @vRAMTest BTST #3,D0 ; 512K machines get one additional mode in all display flavors BNE.S @ShowDown ; ADDQ #1,D2 ; reflect the additional mode @ShowDown CMP.B D2,D1 ; see if we're OK BGT.S @ModeBad ; if D2>D1, it's an illegal mode @ModeOK CMP.W D1,D1 ; get EQ @ModeBad MOVEM.L (SP)+,D0/D2-D3 ; restore trashed regs @ModeOut RTS ; EQ if valid depth ENDWITH ;--------------------------------------------------------------------- ; ; Wait for vertical blanking. Interrupts are raised to level-2 around ; this routine. ; ; A1 = DCE POINTER ;--------------------------------------------------------------------- V8WaitVSync MOVE.L A0,-(SP) ; save work registers <8> MOVE.L D0,-(SP) ; (two MOVEs are faster than a MOVEM) <8> MOVE.L V8,A0 ; point to VISA MOVE.B #$40,V8SInt(A0) ; clear the interrupt, just in case, ; since the intHndlr can't get ; called here ADDA #V8SInt,A0 ; point to interrupt register @0 MOVE.B (A0),D0 ; Read the Vert-Sync state BTST #6,D0 ; BNE.S @0 ; Repeat until it goes low ; MOVE.L (SP)+,D0 ; restore work registers <8> MOVE.L (SP)+,A0 ; (two MOVES are faster than a MOVEM) <8> RTS V8SetDepth ;--------------------------------------------------------------------- ; ; SetDepth sets the V8 frame buffer depth, and returns the ; frame buffer base in driver privates ; ; D0 contains the page number ; D1 contains the spID of the depth - $80 (the zero based mode ID) ; A2 = parameter block pointer ; A3 = dCtlStorage pointer ; ; Preserves all registers ; ;--------------------------------------------------------------------- WITH V8VidPrivates MOVEM.L D2/D3/A0/A1,-(SP) ; save regs we are using <1.8> MOVE.B D1,D3 ; make a copy of the depth value ; Test for Apple // mode. If it is this mode, then we need to set the V8 to the next greater ; depth. MOVE.B saveSlotId(A3),D2 ; get spID in a register AND.B #$F7,D2 ; don't look at the vRAM size bit CMP.B #sRsrc_Vid_V8_A2Ema,D2 ; compare to 512K Apple // mode BNE.S @0 ADDQ #1,D3 ; bump the depth up by one @0 MOVE.L V8,A0 ; point to Elsie base MOVE.B V8MonP(A0),D2 ; get the V8 state AND.B #ClrDepthBitsMask,D2 ; clear the lo-3 bits (depth control) OR.B D3,D2 ; turn on the depth bits MOVE.B D2,V8MonP(A0) ; set the mode ; set up the Ariel MOVE.L saveVDACBase(A3),A0 ; point to the DAC MOVE.B V8DACrCntlReg(A0),D2 ; get the Ariel control register value AND.B #ClrDepthBitsMask,D2 ; clear the depth bits OR.B D1,D2 ; set the new depth MOVE.B D2,V8DACwCntlReg(A0) ; set the new control register value @CalcBase MOVE.W csMode(A2),saveMode(A3) ; save mode number CMP.W #FifthVidMode,saveMode(A3) ; was it 16-bit mode? BNE.S @BitOff ; no, so turn flag off BSET #IsDirect,GFlags(A3) ; turn on bit BRA.S @Out ; @BitOff BCLR #IsDirect,GFlags(A3) ; turn off bit @Out MOVEM.L (SP)+,D2/D3/A0/A1 ; restore all regs <1.8> RTS ; return ENDWITH V8GrayScreen ;--------------------------------------------------------------------- ; ; 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 ; A3 = driver private storage ; ; All registers are preserved ; WITH V8VidPrivates MOVEM.L D0-D3/A0-A1,-(SP) ; save all registers LEA V8Pats,A1 ; get a pointer to the pattern table MOVE.L (A1,D1*4),D3 ; D3 = the proper pattern MOVEQ.L #$17,D0 ; make a mask for pertinent bits AND.B saveSlotId(A3),D0 ; get the monitor+upper option bit CMP.B #sRsrc_Vid_V8_GSa**$17,D0 ; is it a standard Rubik? BNE.S @0 ; if not, continue LEA V8GSParms,A1 ; BRA.S @Blast2 @0 CMP.B #sRsrc_Vid_V8_A2Ema**$17,D0 ; is it a Rubik in A// mode? BNE.S @1 ; if not, continue LEA V8A2Parms,A1 ; BRA.S @Blast2 ; if it's none of the above it's either a High-Res w/vRAM or a VGA which use the same parameters @1 LEA V8HRvRAMParms,A1 ; @Blast2 BTST #3,saveSlotID(A3) ; if this bit is cleared, then 512K vRAM BNE.S @ItsOK ; if bit set, then 256K vRAM so parm addr is OK ; ¥¥¥!!!¥¥¥ ADDA #LongParmShift,A1 ; advance A1 to 512K parameters ADDA #58,A1 ; advance A1 to 512K parameters @ItsOK MOVE.L saveBaseAddr(A3),A0 ; get the frame buffer base address MOVE.W (A1)+,D0 ; get the # of rows @NxtRow MOVE.W (A1,D1*4),D2 ; get the # of longs/row @NxtLong MOVE.L D3,(A0)+ ; write gray DBRA D2,@NxtLong ; for each scanline ADD.W 2(A1,D1*4),A0 ; add the skip factor for each line NOT.L D3 ; invert pattern on next row DBRA D0,@NxtRow ; for each row MOVEM.L (SP)+,D0-D3/A0-A1 ; restore registers RTS V8Pats DC.L OneBitGray,TwoBitGray,FourBitGray,EightBitGray,SixteenBitGray V8DirectCLUTSet ;--------------------------------------------------------------------- ; ; V8DirectCLUTSet writes gamma-corrected ascending grayscale ramps into ; the CLUT ; ; A3 = dCtlStorage pointer ; ; Preserves all registers ; ;--------------------------------------------------------------------- MOVEM.L D0-D2/A0/A4-A6,-(SP) ; save a register 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 #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 ADD #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 ASL #1,D0 ; mulu times 2 ADDA D1,A6 ; calc base of blue @OneTbl MOVE.L saveVDACBase(A3),A0 ; point to the hardware ADDA #V8DACwDataReg,A0 ; point to data register CLR.B V8DACwAddReg-V8DACwDataReg(A0) ; start at the beginning of CLUT MOVE.W #$1F,D2 ; set up counter MOVE.W SR,-(SP) ; preserve the status register MOVEQ #7,D0 ; get mask in 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 ; write an incrementing grayscale ramp MOVEQ #0,D0 ; set up ramp start @Repeat MOVE.L D0,D1 ; copy the position pointer SWAP D1 ; get the ÒintegerÓ part MOVE.B (A4,D1),(A0) ; put red MOVE.B (A5,D1),(A0) ; put green MOVE.B (A6,D1),(A0) ; put blue ADD.L #$00083900,D0 ; add 8 and 7/31 to the number DBRA D2,@Repeat ; MOVE (SP)+,SR ; restore the status reg MOVEM.L (SP)+,D0-D2/A0/A4-A6 ; restore saved registers RTS ; ; some simple screen size parameters. The first number is the number of scanlines to ; erase for each mode. The next numbers are in pairs. The first element of each pair is the number ; of longs to gray per scanline. The second element is the skip factor (number of bytes to skip ; to get to beginning of the next scanline). The second set of numbers are the same info for the ; 512K vRAM version (which has 1024 rowbytes rather than 512). There are more modes in the second ; set, but the code in V8GrayScreen knows how to deal with it ; V8GSParms DC.W defmBounds_BGS-1 DC.W (((defmBounds_RGS*1)/8)/4)-1,(V8_512_RB-((defmBounds_RGS*1)/8)) DC.W (((defmBounds_RGS*2)/8)/4)-1,(V8_512_RB-((defmBounds_RGS*2)/8)) DC.W (((defmBounds_RGS*4)/8)/4)-1,(V8_512_RB-((defmBounds_RGS*4)/8)) DC.W (((defmBounds_RGS*8)/8)/4)-1,(V8_512_RB-((defmBounds_RGS*8)/8)) DC.W 0,0 ; pad out table V8HRvRAMParms DC.W defmBounds_BHR-1 DC.W (((defmBounds_RHR*1)/8)/4)-1,(V8_512_RB-((defmBounds_RHR*1)/8)) DC.W (((defmBounds_RHR*2)/8)/4)-1,(V8_512_RB-((defmBounds_RHR*2)/8)) DC.W (((defmBounds_RHR*4)/8)/4)-1,(V8_512_RB-((defmBounds_RHR*4)/8)) DC.W 0,0 ; pad out table V8A2Parms DC.W defmBounds_BA2Em-1 DC.W (((defmBounds_RA2Em*1)/8)/4)-1,(V8_512_RB-((defmBounds_RA2Em*1)/8)) DC.W (((defmBounds_RA2Em*2)/8)/4)-1,(V8_512_RB-((defmBounds_RA2Em*2)/8)) DC.W (((defmBounds_RA2Em*4)/8)/4)-1,(V8_512_RB-((defmBounds_RA2Em*4)/8)) DC.W 0,0 ; pad out table V8GSParms_1024 DC.W defmBounds_BGS-1 DC.W (((defmBounds_RGS*1)/8)/4)-1,(V8_1024_RB-((defmBounds_RGS*1)/8)) DC.W (((defmBounds_RGS*2)/8)/4)-1,(V8_1024_RB-((defmBounds_RGS*2)/8)) DC.W (((defmBounds_RGS*4)/8)/4)-1,(V8_1024_RB-((defmBounds_RGS*4)/8)) DC.W (((defmBounds_RGS*8)/8)/4)-1,(V8_1024_RB-((defmBounds_RGS*8)/8)) DC.W (((defmBounds_RGS*16)/8)/4)-1,(V8_1024_RB-((defmBounds_RGS*16)/8)) V8HRvRAMParms_1024 DC.W defmBounds_BHR-1 DC.W (((defmBounds_RHR*1)/8)/4)-1,(V8_1024_RB-((defmBounds_RHR*1)/8)) DC.W (((defmBounds_RHR*2)/8)/4)-1,(V8_1024_RB-((defmBounds_RHR*2)/8)) DC.W (((defmBounds_RHR*4)/8)/4)-1,(V8_1024_RB-((defmBounds_RHR*4)/8)) DC.W (((defmBounds_RHR*8)/8)/4)-1,(V8_1024_RB-((defmBounds_RHR*8)/8)) V8A2Parms_1024 DC.W defmBounds_BA2Em-1 DC.W (((defmBounds_RA2Em*1)/8)/4)-1,(V8_1024_RB-((defmBounds_RA2Em*1)/8)) DC.W (((defmBounds_RA2Em*2)/8)/4)-1,(V8_1024_RB-((defmBounds_RA2Em*2)/8)) DC.W (((defmBounds_RA2Em*4)/8)/4)-1,(V8_1024_RB-((defmBounds_RA2Em*4)/8)) DC.W (((defmBounds_RA2Em*8)/8)/4)-1,(V8_1024_RB-((defmBounds_RA2Em*8)/8)) V8HRParms DC.W defmBounds_BHR-1 DC.W (((defmBounds_RHR*1)/8)/4)-1,(OBMHRRB-((defmBounds_RHR*1)/8)) LongParmShift EQU V8GSParms_1024-V8GSParms ENDWITH ;------------------------------------------------------------- ; The Interrupt handler for the Elsie Built-In Video ;------------------------------------------------------------- ; On entry A1 contains the pointer to the driver's private storage ; D0-D3/A0-A3 have been preserved. V8BeginIH MOVE.L V8,A0 ; point to the VISA chip MOVE.B #$40,V8SInt(A0) ; clear the interrupt CLR.W D0 ; set slot zero in D0 MOVE.L JVBLTask,A0 ; call the VBL task manager JSR (A0) ; with slot # in D0 @Done MOVEQ #1,D0 ; signal that int was serviced RTS ; and return to caller END