boot3/DeclData/DeclVideo/RBV/RBVDriver.a
Elliot Nunn 5b0f0cc134 Bring in CubeE sources
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included.

The Tools directory, containing mostly junk, is also excluded.
2017-12-26 10:02:57 +08:00

1551 lines
52 KiB
Plaintext

;
; 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):
;
; <SM3> 11/5/92 SWC Changed VideoEqu.a->Video.a and ShutdownEqu.a->Shutdown.a.
; <SM2> 11/2/92 kc Don't include SonicEqu.a.
; <1> 10/6/92 GDW New location for ROMLink tool.
; <SM3> 09-03-92 jmp (jmp,H2) Corrected .s vs. non-.s branches and odd-alignment
; problems.
; <SM2> 5/16/92 kc Roll in Horror Changes. Comments follow:
; <T2> 12/11/90 HJR Integration of Tim and V8 Video into the Terror Project.
; <T7> 6/12/90 JJ Add support for Elsie Apple II emulation video modes.
; <T6> 4/30/90 JJ Minor formatting changes.
; <H4> 4/3/90 CV Moved the search string to find the video driver overpatch to
; 'DeclDataRBV.a'.
; <H3> 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