sys7.1-doc-wip/DeclData/VSCDeclData/VSCDriver.a
2019-07-27 22:37:48 +08:00

3222 lines
114 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;
; File: VSCDriver.a
;
; Contains: This file contains the video driver for use by the Macintosh
; OS for the VSC hardware.
;
; Written by: Gary Rensberger, based on Mike Puckets SonoraDriver. January 5, 1991.
;
; Copyright: © 1991-1993 by Apple Computer, Inc., all rights reserved.
;
; Change History (most recent first):
;
; <SM2> 12/13/93 PN Roll in KAOs and Horror changes to support Malcom and AJ
; machines
; <1> 12-04-92 jmp first checked in
; ———————————————————————————————————————————————————————————————————————————————————————
; Pre-SuperMario comments begin here.
; ———————————————————————————————————————————————————————————————————————————————————————
; <H9> 7/14/92 djw (HJR) Changed VSCPowerOnPlanes so that it set the VSC Hardware
; to an initial default state so that if the driver is closed,
; opening the driver will allow the user to work with the VSC.
; Actually VSCPowerOnPlanes does a slightly condensed primary
; init.
; <H8> 7/13/92 HJR Changed the polarity of PmgrExtVidOn. Moved VSCPowerOnPlanes in
; the open till after the Monitor is identified.
; <H7> 7/11/92 HJR Call VSCEnableVGuts/VSCDisableVGuts in VSCLowPwrSelect.
; <H6> 6/30/92 HJR Install TimeMgr PsuedoVBL when in lower power state so that
; cursor tasks are updated.
; <H5> 6/2/92 HJR Set in supervisor mode in VSCInstallBusErrH and VSCRemoveBusErrH
; before doing priviledged instructions so that VM is happy.
; <H4> 6/1/92 HJR Added VSCPowerOnPlanes, VSCPowerDownPlanes to Utility routines.
; Also shutoff power on video close.
; <H3> 5/19/92 HJR Completely changed how VSCPowerSelect control call operates.
; Added a new Bus Error handler so that when screen is power
; down, frame accesses .don't hang the system.
; <H2> 5/7/92 HJR Added VSCPowerSelect control call to give ability to power
; off/on the VSC. Provided underlining support for the control
; call.
; <1> 4/24/92 HJR first checked in
; <3> 2/6/92 RLE renamed board ID name to be generic so it works for either
; Gemini or Deskbar
; <2> 1/30/92 GMR Changed slot E handler to handle only built-in video.
; <1> 1/28/92 GMR first checked in
;-----------------------------------------------------------------------------------------
BLANKS ON
STRING ASIS
MACHINE MC68020
usingTimeMgr Equ 0 ; If true, were using the TimeMgr to simulate VBLs.
; This is device storage which is stored in the dCtlStorage field of the AuxDCE.
VSCVidPrivates RECORD 0
saveMMUMode DS.L 1 ; holds old mode when switching to 32 bit mode
saveScreenBase DS.L 1 ; the screen base address
saveGammaPtr DS.L 1 ; the pointer to the Gamma correction table
saveGamDispPtr DS.L 1 ; the pointer to the Gamma block
saveVidPtr DS.L 1 ; the pointer to the vidParams block
saveVDACBase DS.L 1 ; the base addr of the VDAC
saveAIV3Base DS.L 1 ; the base addr of AIV3
saveVideoBase DS.L 1 ; the base addr of Video regs
saveVSCConfigParm DS.L 1 ; the config parameters for waking up
saveBusErrVect DS.L 1 ; the systems current bus error handler
GFlags DS.W 1 ; flags word (hi-order byte, actually)
saveMode DS.W 1 ; the current mode setting
saveMonID DS.W 1 ; monitor type ID
saveSizeVRAM DS.B 1 ; amount of vRAM (0=512K,1=1024)
savePowerStatus DS.B 1 ; current state of clocks and power to VSC
saveParams DS.B 2 ; parameters for the VSC in order to power down and up
TTask DS.B tmXQSize ; extended time manager task block
IntDisableFlag DS.W 1 ; this word is non-zero when the VBL interrupt are disabled
saveSlot Ds.b 1 ; our slot number
sleepWakeFlag Ds.b 1 ; if true, we doing the sleep/wake call (not dim/undim)
saveSQElPtr DS.L 1 ; the SQ element pointer (for _SIntRemove)
VSCVidPrivSize EQU *
ENDR
kVSCVBLTime EQU -16626 ; 60.14742 Hz using the microsecond timer.
;-------------------------------------------------------------------
; Video Driver Header
;-------------------------------------------------------------------
;
VSCVidDrvr
DC.W $4C00 ; ctl,status,needsLock
DC.W 0,0,0 ; not an ornament
; Entry point offset table
DC.W VSCVidOpen-VSCVidDrvr ; open routine
DC.W VSCVidDrvr-VSCVidDrvr ; no prime in normal video drivers
DC.W VSCVidCtl-VSCVidDrvr ; control
DC.W VSCVidStatus-VSCVidDrvr ; status
DC.W VSCVidClose-VSCVidDrvr ; close
STRING Pascal
VSCVidTitle
DC.B '.Display_Video_Apple_ViSC'
ALIGN 2 ; make sure we're aligned
DC.W CurVSCDrvrVersion ; current version
STRING ASIS
;
; VSCCLUTTbl contains information required to write to the CLUT in the different screen depths.
; Each depth's information has three values. The first is the number of entries-1 in this depth
; for range checking. The second is the address of the first active CLUT position for that
; screen depth. The last number is the “skip” factor. This is required because, in 1-4bpp, the
; entries are distributed throughout the CLUT address space. As a result, we use sequential CLUT mode
; ONLY in 8/16bpp modes. The skip factor is the address difference between adjacent active positions
; in each mode.
;
; Generally, these rules are true for any particular depth:
; #entries = (2^^depth)-1
; startposition = (256 / (2^^depth))-1
; skipfactor = 256 / (2^^depth)
VSCCLUTTbl
DC.B $01,$7F,$00,$80 ; for one-bit mode
DC.B $03,$3F,$00,$40 ; for two-bit mode
DC.B $0F,$0F,$00,$10 ; for four-bit mode
DC.B $FF,$00,$00,$01 ; for eight-bit mode
DC.B $1F,$00,$00,$01 ; for sixteen-bit mode
VSCCLUTRec RECORD 0 ;
scRange DS.B 1 ; maximum index value in this depth
scStart DS.B 1 ; lowest active CLUT address
scSkip DS.W 1 ; skip value between active addresses
VSCCLUTSize Equ *
ENDR
;
; These are the bit patterns for grays in each depth
;
VSCPats Dc.l OneBitGray,TwoBitGray,FourBitGray,EightBitGray,SixteenBitGray
;
; The following tables are used to support the Get/SetAltSense codes. The first set of
; tables are pairs of codes and indices, terminated by a -1. Each of these tables
; is composed of a particular sense-code types (i.e., 3, 5, 6, and 7). The final
; table is also composed of pairs -- the first value is the sense-code type, and
; the corresponding second value is an offset to the sense-code index tables.
;
VSCType_0_MonIDs
Dc.b indexedSenseFP, indexedSenseFP
Dc.b indexedSenseRubik, indexedSenseRubik
Dc.b indexedSenseRGBFP, indexedSenseRGBFP
Dc.b indexedSenseHR, indexedSenseHR
Dc.b indexedNoConnect, indexedNoConnect
Dc.w -1
VSCType_3_MonIDs
Dc.w -1
VSCType_6_MonIDs
Dc.b extendedMSB1, indexedSenseMSB1
Dc.b extendedMSB2, indexedSenseMSB2
Dc.b extendedMSB3, indexedSenseMSB3
Dc.w -1
VSCType_7_MonIDs
Dc.b extendedSenseVGA, indexedSenseVGA
Dc.b extendedSenseGF, indexedSenseGF
Dc.b extendedNoConnect, indexedNoConnect
Dc.w -1
VSCMonIDsTbl Dc.w 0, VSCType_0_MonIDs-VSCMonIDsTbl
Dc.w 3, 0
Dc.w 5, 0
Dc.w 6, VSCType_6_MonIDs-VSCMonIDsTbl
Dc.w 7, VSCType_7_MonIDs-VSCMonIDsTbl
Dc.w -1
;
; The following table is a list of CPUFlags and handler offsets to the type
; of bus errors this code currently handles.
;
VSCValidCPUs
Dc.w cpu68020, VSCBusErrHandler030-VSCValidCPUs
Dc.w cpu68030, VSCBusErrHandler030-VSCValidCPUs
Dc.w cpu68040, VSCBusErrHandler040-VSCValidCPUs
Dc.w -1
; The following table is used by the SwitchMode call to determine two things. First,
; it must decide whether the requested mode to switch to is valid or not. If
; the requested mode is not in the SwitchTable, the call will fail. Also, even
; if the requested mode is in table, if the current bit depth is too large
; for the requested mode, SwitchMode must still fail.
;
SwitchTable
Dc.b sRsrc_Vid_VSC_FPa, ThirdVidMode ; Full-Page Display
Dc.b sRsrc_Vid_VSC_FPb, FourthVidMode
Dc.b sRsrc_Vid_VSC_RGBFPa, ThirdVidMode ; RGB Full-Page
Dc.b sRsrc_Vid_VSC_RGBFPb, FourthVidMode
Dc.b sRsrc_Vid_VSC_VGA, FourthVidMode ; VGA
Dc.b sRsrc_Vid_VSC_SVGA, FourthVidMode ;
Dc.b sRsrc_Vid_VSC_MSB1, FourthVidMode ; MSB1
Dc.b sRsrc_Vid_VSC_GS, FourthVidMode ;
Dc.b sRsrc_Vid_VSC_GF, FourthVidMode ;
Dc.b sRsrc_Vid_VSC_MSB2, FourthVidMode ; MSB1/MSB2
Dc.b sRsrc_Vid_VSC_HR, FourthVidMode ;
Dc.b sRsrc_Vid_VSC_1K, ThirdVidMode ;
Dc.b 0,0 ; End
; The following table is used by the GetConnection call to fill out the appropriate
; information for the Display Manager per on a display-by-display basis.
;
ConnectEntrySz Equ 8 ; Size of ConnectTable Entry.
VSCConnectTable
Dc.w indexedSenseFP ; Full-Page Display
Dc.w kFullPageConnect
Dc.l (1<<kAllModesValid)|(1<<kAllModesSafe)|\
(1<<kIsMonoDev)
Dc.w indexedSenseRGBFP ; RGB Full-Page
Dc.w kFullPageConnect
Dc.l (1<<kAllModesValid)|(1<<kAllModesSafe)
Dc.w indexedSenseVGA ; VGA
Dc.w kVGAConnect
Dc.l (1<<KAllModesValid)
Dc.w indexedSenseMSB1 ; MSB1
Dc.w kMultiModeCRT1Connect
Dc.l (1<<KAllModesValid)
Dc.w indexedSenseMSB2 ; MSB2
Dc.w kMultiModeCRT2Connect
Dc.l (1<<KAllModesValid)
Dc.w indexedSenseMSB3 ; MSB3
Dc.w kMultiModeCRT3Connect
Dc.l (1<<KAllModesValid)
Dc.w -1 ; End
VSCDfltConnect Dc.w kFixedModeCRTConnect ; For everyone else.
Dc.l (1<<KAllModesValid)
**********************************************************************
*
* VidOpen allocates private storage for the device in the AuxDCE and locks
* it down for perpetuity. Also, it installs the interrupt handler and enables
* the (VBL) interrupts.
*
* Entry: A0 = param block pointer
* A1 = AuxDCE pointer
*
* Locals: A3 = pointer to private storage
*
* A4/D2/D3 used as scratch
*
**********************************************************************
WITH VDPageInfo,SlotIntQElement,VSCVidPrivates
VSCVidOpen
;
; Allocate private storage (since block is CLEAR, GFlags are zeroed) and get
; a pointer to it in A3
;
MOVEQ #VSCVidPrivSize,D0 ; get size of parameters
_ResrvMem ,SYS ; make room as low as possible
MOVEQ #VSCVidPrivSize,D0 ; get size of parameters
_NewHandle ,SYS,CLEAR ; get some memory for private storage
BNE @OpError1 ; => return an error in open
MOVE.L A0,dCtlStorage(A1) ; save returned handle in AuxDCE
_HLock ; and lock it down forever
MOVE.L (A0),D0 ; get a pointer to it
_StripAddress ; clean it up
MOVE.L D0,A3 ; get pointer to privates in A3
;
; Remember the VDAC, VSC, and framebuffer base addresses since they're non-trivial to
; look up. (Unless, of course, you just hard-code them like what has been done
; here :-)
;
move.l #VDACBase,saveVDACBase(A3) ; save VDACs base address
move.l #AIV3Base,saveAIV3Base(A3) ; save VSCs VIA base address
move.l #VSCVideoBase,saveVideoBase(A3) ; save VSCs Video Registers base address
move.l #VRAMBase,saveScreenBase(A3) ; save VRAM base address too
;
; Get and install the interrupt handler. Call the EnableVGuts utility code to do
; this. This utility also starts the interrupts going. If there is an error
; condition, EnableVGuts returns with the Z-bit cleared.
If usingTimeMgr Then
LEA VSCTimeMgrIH,A0 ; get a pointer to the interrupt simulation timer task code
MOVE.L A0,tmAddr+TTask(A3) ; put it in the time task
LEA TTask(A3),A0 ; get a pointer to the timer queue element
Move.l #'eada',(A0) ; Put in the magic signature to prevent VM from deferring us.
_InsXTime ; Install the task (fixed frequency).
Else
MOVEQ #sqHDSize,D0 ; allocate a slot queue element
_NewPtr ,SYS,CLEAR ; get it from system heap cleared
BNE @OpError2 ; if not allocated, return bad
MOVE.L A0,saveSQElPtr(A3) ; save the SQ element pointer.
Endif
Move.b dCtlSlot(A1),saveSlot(A3) ; Remember our slot number.
Bsr VSCEnableVGuts ; do it
Bne @OpError2 ;
;
; Load the default gamma table from the slot resource list.
;
WITH SpBlock
SUBA #spBlockSize,SP ; make a slot parameter block
MOVE.L SP,A0 ; get pointer to block in A0
MOVE.B dCtlSlot(A1),spSlot(A0) ; copy the slot number
MOVE.B dCtlSlotId(A1),spID(A0) ; copy the spID of the video sRsrc
CLR.B spExtDev(A0) ;
_sRsrcInfo ; get the spsPointer
BNE @OpError3 ; if failed, then quit.
MOVE.B #sGammaDir,spID(A0) ; look for the gamma directory
_sFindStruct ; get that baby
BNE.S @DoLinear ; if failed, then do linear
MOVE.B #128,spID(A0) ; get the default gamma table, (always 128)
_sGetBlock ; we can use this since we want it on the sys heap
BNE.S @DoLinear ; if failed, then do linear
; Skip over gamma header.
MOVE.L spResult(A0),A0 ; point to head of the block
MOVE.L A0,saveGamDispPtr(A3) ; save the ptr to the gamma block
ADDA #2,A0 ; skip resID
@Name TST.B (A0)+ ; skip over gamma name
BNE.S @Name ;
MOVE.L A0,D0 ; get in d-reg
ADDQ #1,D0 ; word align pointer
BCLR #0,D0 ; round it off
MOVE.L D0,saveGammaPtr(A3) ; put it in private storage
Bra.s @VidParams ; Jump around linear code.
;
; Build a linear default gamma table if necessary.
;
@DoLinear
Moveq #gFormulaData,D0 ; Get gamma table header size.
Add #256,D0 ; Add in one-byte per entry.
_NewPtr ,SYS,CLEAR ; Clear it.
Bne @OpError3 ; If failed, quit.
Move.l A0,saveGamDispPtr(A3) ; Save head of gamma table for disposal.
Move.l A0,saveGammaPtr(A3) ; Head and top are same here.
Move.w #drHwVSC,gType(A0) ; Set up gType.
Move.w #1,gChanCnt(A0) ; Set up gChanCnt.
Move.w #256,gDataCnt(A0) ; Set up gDataCnt.
Move.w #8,gDataWidth(A0) ; Set up gDataWidth.
Adda #gFormulaData+256,A0 ; Point to end of data table.
Move.w #255,D0 ; Set up loop counter.
@Loop Move.b D0,-(A0) ; Write out value.
Dbra D0,@Loop ; Loop.
;
; Get a pointer to the video hardware setup parameter block. Use this functional spID's spsPointer
; found above in the gamma section.
;
@VidParams
Move.l Sp,A0 ; Point to the spBlock on the stack.
Move.b dCtlSlot(A1),spSlot(A0) ; Get the slot number.
Move.b #sRsrc_Board,spID(A0) ; Get the appropriate board sRsrc ID.
Clr.b spExtDev(A0) ;
_sRsrcInfo ; Get the spsPointer.
Bne @OpError4 ; If failed, quit.
MOVE.B #sVidParmDir,spID(A0) ; look for the video parameters dir
_sFindStruct ; Try to load it.
Bne @OpError4 ; If failed, quit.
MOVE.B dCtlSlotId(A1),spID(A0) ; look in the directory for this config's parameters
_sGetBlock ; Try to load it.
Bne @OpError4 ; If failed, quit.
MOVE.L spResult(A0),saveVidPtr(A3) ; get pointer to it
;
; At PrimaryInit time, we used the sense lines to determine the type of attached display. For extended
; sense displays, we just mapped them to the end of indexed-sense displays. Since the gamma-correction
; code uses the monitor ID to determine if the passed-in table is applicable, we need to know the “real”
; monitor ID. At PrimaryInit time, we store the real monitor ID in slot pRAM. So, we extract that
; information out here. Also, it should be noted that it would actually be inappropriate for us
; to re-read the sense-lines now, in that someone could potentially change/unplug the attached
; display between PrimaryInit and VidOpen, and that would cause us all sorts of havoc.
;
With SP_Params
Move.l Sp,A0 ; Point to spBlock on the stack.
Move.b dCtlSlot(A1),spSlot(A0) ; Put slot into spBlock.
Suba #sizeSPRamRec,Sp ; Allocate an SPRam block on the stack.
Move.l Sp,spResult(A0) ; Point to it.
_SReadPRAMRec ; Read Slot PRam.
Bne @OpError5 ; If failed quit.
Moveq #0,D2 ; Clear D2.w.
Move.b SP_MonID(Sp),D2 ; Get the monID (its byte sized).
Move.b SP_Flags(Sp),D1 ; Get the vRAM size.
Adda #sizeSPRamRec+spBlockSize,Sp ; Clean up the stack.
;
; Do a little bookkeeping…
;
Move.w D2,saveMonID(A3) ; Save the monID for later.
Bfextu D1{spVRamBits:numSPVRamBits},D1 ; Extract the vRAM size from the flags byte…
Move.b D1,saveSizeVRAM(A3) ; …and save it for later.
Endwith
;
; Set GFlags to reflect monochrome-only displays.
;
Cmp.w #indexedSenseFP,saveMonID(A3) ; If this is a Mono-Only Full Page,
Beq.s @SetMonoFlags ; then say so.
Bra.s @AllDone ; Otherwise, skip.
@SetMonoFlags Bset #IsMono,GFlags(A3) ; Turn on the IsMono and
Bset #GrayFlag,GFlags(A3) ; GrayFlag flags.
;
; All done!
;
@AllDone MOVEQ #noErr,D0 ; no error
@EndOpen RTS ; return
@OpError5
Adda #sizeSPRamRec,Sp ; Release the SPRam block.
@OpError4
Move.l saveGamDispPtr(A3),A0 ; Set up to dispose of gamma table.
_DisposPtr ; Dispose it.
@OpError3
ADDA #spBlockSize,SP ; release the spBlock
@OpError2
MOVE.L dCtlStorage(A1),A0 ; get the private storage back
_DisposHandle ; release the driver private storage
@OpError1
MOVE.L #OpenErr,D0 ; say can't open driver
BRA.S @EndOpen
ENDWITH
**********************************************************************
*
* Video Driver Control Call Handler. There are 11 standard calls:
*
* ($00) Reset (VAR mode, page: INTEGER; VAR BaseAddr: Ptr);
* ($01) KillIO
* ($02) SetMode(mode, page: INTEGER; VAR BaseAddr: Ptr);
* ($03) SetEntries (Table: Ptr; Start,Count : integer );
* ($04) SetGamma (Table : Ptr );
* ($05) GrayPage (page);
* ($06) SetGray (csMode = 0 for color, 1 for gray)
* ($07) SetInterrupt (csMode = 0 for enable, non-zero for disable);
* ($08) DirectSetEntries (Table: Ptr; Start,Count : integer );
* ($09) SetDefaultMode (csMode = mode to set);
* ($0A) SwitchMode(csMode, csData, csPage, csBaseAddr);
*
* The following calls are VSC-specific:
*
* ($83) SetAltSense(csMode = monitor ID to set)
* ($84) PowerSelect(csMode = 0 for powerup, non-zero for powerdown);
*
* Entry: A0 = param block pointer
* A1 = AuxDCE pointer
* Uses: A2 = cs parameters (ie. A2 <- csParam(A0)) (must be preserved)
* A3 = ptr to our privates/scrarch (doesnt 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…
;
VSCVidCtl
MOVEM.L A0/A1,-(SP) ; Save exit registers.
MOVE.L csParam(A0),A2 ; A2 <- Ptr to control parameters
MOVE.L dCtlStorage(A1),A3 ; A3 <- Ptr to private storage
MOVE.L (A3),D0 ;
_StripAddress ;
MOVE.L D0,A3 ;
MOVE.W csCode(A0),D0 ; get routine selector
Cmpi.w #cscSleepWake,D0 ; If we got the sleep-wake call,
Beq VSCSleepWake ; then hop to it.
CMP.W #cscPowerSelect,D0 ; If we got the Power Select
BEQ VSCLowPwrSelect ; do it
TST.B savePowerStatus(a3) ; IF not powered up THEN
BNE.S VSCCtlBad ; exit (make no control calls, dammit!)
CMP.W #$0A,D0 ; IF csCode NOT IN [0..A] THEN
BHI.S VSCCtlNextRange ; maybe next range?
MOVE.W VSCCtlJumpTbl(PC,D0.W*2),D0 ; Get the relative offset to the routine.
JMP VSCCtlJumpTbl(PC,D0.W) ; GOTO the proper routine.
VSCCtlJumpTbl
DC.W VSCVidReset-VSCCtlJumpTbl ; $00 => VidReset
DC.W VSCCtlGood-VSCCtlJumpTbl ; $01 => CtlGood (no async routines here)
DC.W VSCSetVidMode-VSCCtlJumpTbl ; $02 => SetVidMode
DC.W VSCSetEntries-VSCCtlJumpTbl ; $03 => SetEntries
DC.W VSCSetGamma-VSCCtlJumpTbl ; $04 => SetGamma
DC.W VSCGrayPage-VSCCtlJumpTbl ; $05 => GrayPage
DC.W VSCSetGray-VSCCtlJumpTbl ; $06 => SetGray
DC.W VSCSetInterrupt-VSCCtlJumpTbl ; $07 => SetInterrupt
DC.W VSCDirectSetEntries-VSCCtlJumpTbl ; $08 => DirectSetEntries
DC.W VSCSetDefaultMode-VSCCtlJumpTbl ; $09 => SetDefaultMode
Dc.w VSCSwitchMode-VSCCtlJumpTbl ; $0A => SwitchMode
VSCCtlNextRange
CMP.W #cscAltSense,D0 ; If we got the AltSense call,
BEQ VSCSetAltSense ; hop to it.
VSCCtlBad MOVEQ #controlErr,D0 ; else say we don't do this one
BRA.S VSCCtlDone ; and return
VSCCtlGood MOVEQ #noErr,D0 ; return no error
VSCCtlDone MOVEM.L (SP)+,A0/A1 ; Restore Exit registers.
BRA VSCExitDrvr
VSCVidReset
;---------------------------------------------------------------------
;
; Reset the card to its default
;
;---------------------------------------------------------------------
WITH VSCVidPrivates
MOVE #FirstVidMode,csMode(A2) ; return default mode
MOVE #FirstVidMode,saveMode(A3) ; remember FirstVidMode as the requested mode
MOVEQ #0,D1 ; get default depth in D1 (#firstVidMode-#firstVidMode)
MOVEQ #0,D0 ; get page in D0
MOVE D0,csPage(A2) ; return the page
BSR VSCSetDepth ; set the depth from D1
BCLR #IsDirect,GFlags(A3) ; turn off direct mode bit
Move.l saveScreenBase(A3),csBaseAddr(A2) ; return the base address
BSR VSCGrayScreen ; paint the screen gray
BRA.S VSCCtlGood ; => no error
ENDWITH
VSCSetVidMode
;---------------------------------------------------------------------
;
; 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.
;
; A1 = Ptr to AuxDCE
; A2 = Ptr to cs parameter record
; A3 = Ptr to driver privates
;
;---------------------------------------------------------------------
WITH VSCVidPrivates
MOVE.W csMode(A2),D1 ; D1 = mode
BSR VSCChkMode ; check mode and convert
BNE.S VSCCtlBad ; => not a valid mode
TST.W csPage(A2) ; only page zero is valid
BNE.S VSCCtlBad ; => not a valid page
; Only set if mode has changed.
VSCSetVidModeGuts
MOVE.W csMode(A2),D2 ; get the mode spID (D1 has the zero-based mode)
CMP saveMode(A3),D2 ; has the mode changed?
BEQ @ModeOK1 ; if not, then skip graying
; Remember the newly requested mode.
MOVE.W D2,saveMode(A3) ; remember requested mode
; Set the entire color table to gray before switching to avoid screen anomalies.
Movem.l A4-A6,-(Sp) ; Save gamma-table registers.
Move.l saveGammaPtr(A3),A0 ; Get pointer to gamma data structure.
Lea gFormulaData(A0),A4 ; Point to first gamma table.
Adda.w gFormulaSize(A0),A4 ;
Move.l A4,A5 ; Point to green data (assuming gChanCnt = 1).
Move.l A4,A6 ; Point to red data (assuming gChanCnt = 1).
Cmp.w #1,gChanCnt(A0) ; If theres only one table,
Beq.s @OnlyOneTable ; then were set.
Move.w gDataWidth(A0),D2 ; Get width of each entry (in bits).
Move.w gDataCnt(A0),D0 ; Get # of entries in table.
Addq #7,D2 ; Round to nearest byte.
Lsr.w #3,D2 ; Get bytes per entry.
Mulu D2,D0 ; Get size of table in bytes.
Adda.w D0,A5 ; Calc base of green (red base + D0).
Adda.w D0,A6 ; Calc base…
Adda.w D0,A6 ; …of blue (red base + D0 + D0).
@OnlyOneTable
Move.w gDataCnt(A0),D3 ; Save number of gamma entries.
MOVE.L saveVDACBase(A3),A0 ; get the VDAC base addr
ADDA #ArielDataReg,A0 ; point to data register
CLR.B ArielAddrReg-ArielDataReg(A0) ; start at the beginning of CLUT
MOVE.W SR,-(SP) ; preserve the status register
BSR VSCWaitVSync ; wait for next blanking period (preserves A0)
; Write out gamma-corrected true-gray CLUT…
;
Move.w D3,D0 ; Init loop counter.
Subq #1,D0 ; Zero base it.
Move.w GFlags(A3),D2 ; Get the GFlags into a convenient register.
Lsr.w #1,D3 ; Get midpoint of table(s).
@Repeat Btst #IsMono,D2 ; If this is not a mono-only display
Beq.s @DoRGB ; then do the standard RGB stuff.
Clr.b (A0) ; Otherwise, just write black out
_CLUTDelay ;
Clr.b (A0) ; to the red & green channels.
_CLUTDelay ;
Bra.s @DoMono ;
@DoRGB Move.b (A4,D3),(A0) ; Write: red,
_CLUTDelay ;
Move.b (A5,D3),(A0) ; green,
_CLUTDelay ;
@DoMono Move.b (A6,D3),(A0) ; blue.
_CLUTDelay ;
Dbra D0,@Repeat
MOVE (SP)+,SR ; restore the status reg
Movem.l (Sp)+,A4-A6 ; Restore gamma-table registers.
BSR VSCSetDepth ; set the depth from D1
; Finish up the bookkeeping.
CMP.W #FifthVidMode,saveMode(A3) ; was it a direct mode?
BLT.S @BitOff ; no, so turn flag off
BSET #IsDirect,GFlags(A3) ; turn on bit
BRA.S @ModeOK1 ;
@BitOff
BCLR #IsDirect,GFlags(A3) ; turn off bit
@ModeOK1 Move.l saveScreenBase(A3),csBaseAddr(A2) ; return the base addr
BRA VSCCtlGood ; return no error
ENDWITH
VSCSetEntries
;---------------------------------------------------------------------
;
; Input :
; csParam -> datablock
; datablock = csTable -> table of colorSpecs (not colortable)
; csStart -> where to start setting, or -1
; csCount -> # of entries to change
;
; This call has two modes. In SEQUENCE mode, csCount entries are changed
; in the CLUT, starting at csStart. In INDEX mode, csCount entries are
; installed into the CLUT at the positions specified by their .value fields.
; This mode is selected by passing csStart = -1. In both cases, entries are
; range-checked to the dynamic range of the video mode (bits/pixel).
;
;---------------------------------------------------------------------
;
; Set the CLUT
; A0 = Ptr to the table
; A1 = Ptr to AuxDCE
; A2 = Ptr to cs parameter record
; A3 = Ptr to private data, later to CLUT constants table
; A4 = Ptr to gamma red table
; A5 = Ptr to gamma green table
; A6 = Ptr to gamma blue table
;
; D0-D3 = Scratch
; D4 = Size of stack color table buffer
; D5 = GFlags word
; D6 = Index range [0..n]
; D7 = gamma channel size in bits
;
;---------------------------------------------------------------------
; Initialize loop.
WITH VSCVidPrivates,VSCCLUTRec
BTST #IsDirect,GFlags(A3) ; are we in a direct mode?
BNE.S VSCCtlBad ; error if so
VSCSEGuts
TST.L csTable(A2) ; Check for a nil pointer
BEQ VSCCtlBad ;
MOVEM.L A1/A4-A6/D4-D7,-(SP) ; save registers for gamma
MOVE.W GFlags(A3),D5 ; get GFlags word in D5
CMP.W #indexEntries,csStart(A2) ; was it indexed mode?
BEQ.S @SkipSeq ; if so, then leave bit off (it's never turned on in GFlags)
Bset #PsuedoIndex,D5 ; turn on the bit that denotes a seq write that was xlated to indexed
Cmp.w #FourthVidMode,saveMode(A3) ; If its not 8bbp or 16bpp
Blt.s @SkipSeq ; need to use “indexed”.
BSET #UseSeq,D5 ; otherwise, turn on sequential mode bit
@SkipSeq
MOVE.L saveGammaPtr(A3),A0 ; get pointer to gamma data structure
MOVE.W gFormulaSize(A0),D0 ; get the size of formula data
LEA gFormulaData(A0,D0),A4 ; red correction table starts here
MOVE.L A4,A5 ; get default pointer to green data
MOVE.L A4,A6 ; get default pointer to blue data
MOVE gDataWidth(A0),D7 ; get width of each entry in bits
CMP #1,gChanCnt(A0) ; if only only one table, we're set
BEQ.S @OneTbl ; => just one table
MOVE gDataCnt(A0),D0 ; get # entries in table
MOVE D7,D1 ; copy it to goof around
ADDQ #7,D1 ; round to nearest byte
LSR #3,D1 ; get bytes per entry
MULU D1,D0 ; get size of table in bytes
ADDA D0,A5 ; calc base of green
ADDA D0,A6 ; calc base of blue
ADDA D0,A6 ; calc base of blue
@OneTbl
;
; Get the maximum number of entries, zero based from a convenient table.
;
MOVE.W saveMode(A3),D1 ; get the current video mode
SUB.W #FirstVidMode,D1 ; convert to a index
Moveq #0,D6 ; clear all of D6 for .w compare.
Lea VSCCLUTTbl,A0 ; Point to the table of CLUT data.
Lea (A0,D1*VSCCLUTSize),A0 ; Point to the right entry.
Move.b scRange(A0),D4 ; Get the range.
;
; Allocate a temporary color table on the stack. We'll pre-process all the entries that will
; change here so we can hit the hardware as quickly as possible.
;
Move.w csCount(A2),D3 ; Get the number of entries to change,
Bmi VSCSEBadExit ; and hike if its out of range.
Cmp.w D4,D3 ; If D3-D4 > 0 (count - range > 0),
Bhi VSCSEBadExit ; then hike.
Tst.w csStart(A2) ; If were doing indexed entries (-1),
Bmi.s @skipStartChk ; then just go on.
Add.w csStart(A2),D3 ; Adjust count for starting position.
Cmp.w D4,D3 ; If D3-D4 > 0 (count - range > 0),
Bhi VSCSEBadExit ; then hike.
Move.w csCount(A2),D3 ; Otherwise, re-get the count.
@skipStartChk
Move.l D3,D4 ; Make a copy of the table size (zero-based).
Addq #1,D4 ; Make it a counting number.
Btst #UseSeq,D5 ; If were not in sequential mode,
Beq.s @IsIndex ; then do the indexed stuff.
Mulu #3,D4 ; Make room for just R,G,B in sequential mode.
Bra.s @AllocIt ; And continue.
@IsIndex Asl.w #2,D4 ; Make room for i,R,G,B in indexed mode.
@AllocIt Sub.w D4,Sp ; Allocate the table on the stack.
;
; Construct the stack version of the color table. It looks like a color table, but each of the
; components is only eight bits (rather than 16).
;
Move.l A3,D6 ; Were about to torch A3, so save it for later.
Move.l A0,A3 ; Save the CLUT table entry pointer.
MOVE.L SP,A0 ; copy the stack buffer pointer
MOVE.L csTable(A2),A1 ; get colorSpec pointer into A1
; Death! Totally out of registers in this routine, so I'm using the top half of D4 (the temp buffer
; size) as the sequence counter used in most video modes to translate sequential requests into
; the indexed write that the hardware needs.
SWAP D4 ; flip the buffer size to the top half
MOVE.W csStart(A2),D4 ; pick up the sequential start position. It might
; be -1 on a true indexed write, but we won't
; use it below if it is.
;
; Write the index if in indexed mode. If in sequential (8/16) mode, blow it off completely,
; since it won't be needed.
@SetupLoop
MOVE.W (A1)+,D0 ; get index
BTST #UseSeq,D5 ; is it sequence mode?
BNE.S @SLSeq ; yup, so go there
Btst #PsuedoIndex,D5 ; If we are doing an “indexed” mode,
Beq.s @IndexPresent ; then go there now.
; This case is a sequential request in a screen depth that does not allow sequential CLUT writes
; (any non-8/16 bit mode). In this case, we substitute the sequence counter for D0 on each
; entry.
Move.w D4,D0 ; Copy the sequence counter to D0.
Addq #1,D4 ; Increment it.
@IndexPresent Mulu.w scSkip(A3),D0 ; Calculate the new position at this depth.
Add.b scStart(A3),D0 ; Add in the first entry offset.
Move.b D0,(A0)+ ; Write out this index.
@SLSeq
MOVE.W (A1)+,D0 ; get red
MOVE.W (A1)+,D1 ; get green
MOVE.W (A1)+,D2 ; get blue
TST D5 ; test hi bit of the flags
BPL.S @NoGray ; if not set, don't luminence map
BTST #IsDirect,D5 ; test for direct mode as well
BNE.S @NoGray ; don't allow luminence mapping in direct mode
; We're luminence mapping here.
MULU #$4CCC,D0 ; multiply by red weight (0.30)
MULU #$970A,D1 ; multiply by green weight (0.59)
MULU #$1C29,D2 ; multiply by blue weight (0.11)
ADD.L D1,D0 ; sum red and green
ADD.L D2,D0 ; blue also
BFEXTU D0{0:D7},D0 ; get gChanWidth bits for gamma table lookup
MOVE.W D0,D1 ; copy into green register
MOVE.W D0,D2 ; copy into blue register
BRA.S @WriteSP ; go on and write it in the stack buffer
@NoGray
BFEXTU D0{16:D7},D0 ; get gChanWidth bits of red
BFEXTU D1{16:D7},D1 ; get gChanWidth bits of green
BFEXTU D2{16:D7},D2 ; get gChanWidth bits of blue
@WriteSP
BTST #IsMono,D5 ; if monochrome display, write black to red & green
BEQ.S @Brighter ; if not, then set all three channels
CLR.B (A0)+ ; write black for red
CLR.B (A0)+ ; and green
BRA.S @Looper ; write out normal blue
@Brighter
MOVE.B (A4,D0),(A0)+ ; write gamma corrected red
MOVE.B (A5,D1),(A0)+ ; write gamma corrected green
@Looper
MOVE.B (A6,D2),(A0)+ ; write gamma corrected blue
DBRA D3,@SetupLoop ; and loop for each entry
Swap D4 ; Put the temp buffer size back in the lo-half.
;
; OK, the stack table is set up. Now let's load the hardware.
;
Move.l D6,A3 ; Get our privates pointer back into A3 (for WaitVSync).
Move.w Sr,-(SP) ; Preserve the status register.
BSR VSCWaitVSync ; Wait for next blanking period (preserves A0/D0).
MOVE.W csCount(A2),D3 ; get the count again
MOVE.L saveVDACBase(A3),A3 ; get the VDAC base
LEA ArielDataReg(A3),A3 ; point to VDAC data register
LEA 2(SP),A0 ; point to the stack buffer again
BTST #UseSeq,D5 ; is it sequence mode?
BNE.S VSCSeqWrite ; yup, sequence mode, so go there
;
; Here's the loop that actually writes to the hardware when in indexed mode.
;
VSCIndexWrite
MOVE.B (A0)+,ArielAddrReg-ArielDataReg(A3) ; write the index value to the CLUT address
_CLUTDelay ;
MOVE.B (A0)+,(A3) ; write red
_CLUTDelay ;
MOVE.B (A0)+,(A3) ; write green
_CLUTDelay ;
MOVE.B (A0)+,(A3) ; write blue
_CLUTDelay ;
DBRA D3,VSCIndexWrite ; and loop
BRA.S VSCSEDone ;
;
; Write the translated starting position for sequence mode.
;
VSCSeqWrite
MOVE.W csStart(A2),d1 ; get sequence start address
MOVE.B d1,ArielAddrReg-ArielDataReg(A3) ; write the sequence start position
_CLUTDelay ;
;
; Here's the loop that actually writes to the hardware when in sequence mode.
;
@SeqLoop
MOVE.B (A0)+,(A3) ; write red
_CLUTDelay ;
MOVE.B (A0)+,(A3) ; write green
_CLUTDelay ;
MOVE.B (A0)+,(A3) ; write blue
_CLUTDelay ;
DBRA D3,@SeqLoop ; and loop
;
; Clean up and go home.
;
VSCSEDone MOVE (SP)+,SR ; restore status register
ADD D4,SP ; release stack buffer
MOVEM.L (SP)+,A1/A4-A6/D4-D7 ; restore registers
BRA VSCCtlGood ; return O-Tay!
VSCSEBadExit
MOVEM.L (SP)+,A1/A4-A6/D4-D7 ; restore registers
BRA VSCCtlBad ; return an error code
ENDWITH
VSCSetGamma
;---------------------------------------------------------------------
;
; Set the gamma table. This call copies the supplied gTable so the
; caller does not have to put the source on the system heap. It
; tests if the gamma table is exactly a match to the currently
; connected monitor, or always allows it if the monitor number in
; the FormulaData is -1. If supplied gamma table ptr is NIL, then
; it loads a linear gamma table into the private table
;
; A0 = Ptr to private storage
; A1 = Ptr to AuxDCE
; A2 = Ptr to cs parameter record
;
;---------------------------------------------------------------------
WITH VSCVidPrivates
; 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 VSCCtlBad ; => no, return error
Tst.w gType(A2) ; Test the hardwareID.
Beq.s @ChangeTable ; If 0, then accept a TFB-style gamma table.
CMP.W #drHwVSC,gType(A2) ; type = VSC?
BNE VSCCtlBad ; => no, return error
TST.W gFormulaSize(A2) ; if gType=VSC, then check for monID in gFormulaData
BEQ.S @ChangeTable ; if zero, then generic, so continue
MOVE.W gFormulaData(A2),D0 ; get the monitor ID this table was intended for
CMP.W saveMonID(A3),D0 ; is this the monitor?
BEQ.S @ChangeTable ; yes, so do it
ADDQ #1,D0 ; was it -1?
BNE VSCCtlBad ; nope, so must be wrong monitor
; If new table is a different size, reallocate memory.
@ChangeTable
MOVE.L saveGammaPtr(A3),A0 ; get current gamma in A0
MOVE gFormulaSize(A2),D0 ; get size of formula in new
CMP gFormulaSize(A0),D0 ; same as current gamma table
BNE.S @GetNew ; =>no, resize pointer
MOVE gChanCnt(A2),D0 ; get number of tables in new
CMP gChanCnt(A0),D0 ; same as current gamma table?
BEQ.S @SizeOK ; => yes, data size ok
BGT.S @GetNew ; => new one is bigger, save old one
@NewSize Move.l saveGamDispPtr(A3),A0 ; if new one is smaller,
_DisposPtr ; dispose old one
CLR.L saveGamDispPtr(A3) ; flag it's been disposed
@GetNew Moveq #0,D0 ; (_NewPtr takes a long, so clear D0 for later.)
MOVE gDataCnt(A2),D0 ; get number of entries
MULU gChanCnt(A2),D0 ; multiply by number of tables
ADD gFormulaSize(A2),D0 ; add size of formula data
ADD #gFormulaData,D0 ; add gamma table header size
_NewPtr ,Sys ; and allocate a new pointer
BNE VSCCtlBad ; => unable to allocate storage
MOVE.L saveGamDispPtr(A3),D0 ; get old gamma table
MOVE.L A0,saveGammaPtr(A3) ; save new gamma table
TST.L D0 ; was there an old one?
BEQ.S @SizeOK ; => no, already disposed
MOVE.L D0,A0 ; else get old table
_DisposPtr ; and dispose of old gamma table
MOVE.L saveGammaPtr(A3),A0 ; get new gamma table back
Move.l A0,saveGamDispPtr(A3) ; save it for disposal
; Copy the gamma table header.
@SizeOK MOVE gChanCnt(A2),D0 ; get number of tables
MOVE gFormulaSize(A2),D1 ; get size of formula data
MOVE gDataCnt(A2),D2 ; get number of entries
MOVE.L (A2)+,(A0)+ ; copy gamma header
MOVE.L (A2)+,(A0)+ ; which is
MOVE.L (A2)+,(A0)+ ; 12 bytes long
; Copy the data.
MULU D0,D2 ; multiply by number of tables
ADD D1,D2 ; add in size of formula data
SUBQ #1,D2 ; get count - 1
@NxtByte MOVE.B (A2)+,D0 ; get a byte
MOVE.B D0,(A0)+ ; move a byte
DBRA D2,@NxtByte ; => repeat for all bytes
Bra.s @GammaDone ; Check to see if its a direct device.
;
; Set up a linear gamma table. To prevent memory thrash, build this new one
; the same size as the existing one (one or three channel).
;
@LinearTab
MOVE.L saveGammaPtr(A3),A0 ; get current gamma in A0
MOVE.W gFormulaSize(A0),D0 ; get size of formula in new
MOVE.W gChanCnt(A0),D2 ; get the number of tables
SUBQ #1,D2 ; zero based, of course
Move.w gDataCnt(A0),D3 ; get the number of entries
Subq #1,D3 ; zero base
ADDA #gFormulaData,A0 ; point to tables
ADDA D0,A0 ; point past monID, if present
@ChanLoop MOVE.W D3,D0 ; loop count within each channel
@entryLoop MOVE.B D0,(A0) ; write this value out
Not.b (A0)+ ; invert to make table ramp properly
DBRA D0,@entryLoop ; for each entry in channel
DBRA D2,@ChanLoop ; and each channel
@GammaDone
BTST #IsDirect,GFlags(A3) ; are we in a direct mode?
BEQ.S @Out ; if not, then we're done
BSR VSCDirectCLUTSet ; if so, then set up direct CLUT ramps
@Out
BRA VSCCtlGood ; => return no error
ENDWITH
VSCGrayPage
;---------------------------------------------------------------------
;
; Clear the specified page in the current mode to gray
;
; A0 = Ptr to private storage
; A1 = Ptr to AuxDCE
; A2 = Ptr to cs parameter record
; A3 = Ptr to driver privates
;
;---------------------------------------------------------------------
WITH VSCVidPrivates
MOVE saveMode(A3),D1 ; D1 = mode
BSR VSCChkMode ; convert mode to depth in D1
BNE VSCCtlBad ; => not a valid depth
MOVE csPage(A2),D0 ; D0 = page
BNE VSCCtlBad ; => not a valid page
BSR VSCGrayScreen ; paint the screen gray
BTST #IsDirect,GFlags(A3) ; are we in a direct mode?
BEQ.S @Out ; if not, then we're done
BSR VSCDirectCLUTSet ; if so, then set up direct CLUT ramps
@Out
BRA VSCCtlGood ; => return no error
ENDWITH
VSCSetGray
;---------------------------------------------------------------------
;
; Set luminance mapping on (csMode = 1) or off (csMode = 0)
;
; When luminance mapping is on, RGB values passed to setEntries are mapped
; to grayscale equivalents before they are written to the CLUT.
;
; A1 = Ptr to AuxDCE
; A2 = Ptr to cs parameter record
;
;---------------------------------------------------------------------
WITH VSCVidPrivates
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 VSCSetIntCom ; call common code
BRA VSCCtlGood ; 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.
;
VSCSetIntCom
MOVE.B csMode(A2),D0 ; get boolean
BFINS D0,GFlags(A3){D1:1} ; set flag bit
RTS ; and return
ENDWITH
VSCSetInterrupt
;---------------------------------------------------------------------
;
; Enable (csMode = 0) or disable (csMode = 1) VBL interrupts
;
; As a future performance enhancement, interrupts on the card can be
; disabled or enabled from software. For instance, if the cursor is
; not on a screen, and there is nothing in the Slot Interrupt Queue
; for that device, interrupts may be disabled reducing interrupt
; overhead for the system.
;
; The slot interrupt queue element is always allocated by the Open call.
; This routine just inserts and removes it from the slot interrupt task queue.
;
; A1 = Ptr to AuxDCE
; A2 = Ptr to cs parameter record
; A3 = Ptr to private storage
;
;---------------------------------------------------------------------
WITH VDPageInfo,SlotIntQElement,VSCVidPrivates
If usingTimeMgr Then
Tst.b csMode(A2) ; Check to see which way were going.
Else
MOVEQ #1,D1 ; set up for BFEXTU to point to IntDisFlag
BSR.S VSCSetIntCom ; call common code
Endif
BNE.S @DisableThem ; If non-zero, then were disabling.
; This code enables interrupts and installs the interrupt handler.
;
BSR.S VSCEnableVGuts ; call common code
BNE VSCCtlBad ; error, flag problem
BRA VSCCtlGood ; and go home
; This code disables VBL interrupts, then removes the interrupt handler.
;
@DisableThem
BSR.S VSCDisableVGuts ; jump to the disabling utility
BRA VSCCtlGood ; all done
; The following two routines are common code shared between the Open/Close calls
; and the SetInterrupt control call.
;
VSCDisableVGuts
If usingTimeMgr Then
Move.w #-1,IntDisableFlag(A3) ; Remember that were disabling things.
Rts
Else
MOVE.W SR,-(SP) ; preserve the status register
BSR VSCWaitVSync ; to be safe, wait for the next VBL
Move.l saveAIV3Base(A3),A0 ; Get the VSC base address.
Move.b #(1<<slotVBL),AIV3SlotEn(A0) ; Disable Slot interrupts.
MOVE.W (SP)+,SR ; re-enable cursor interrupts
MOVEQ #0,D0 ; parameter size to SInstall is not byte sized so clr whole long
MOVE.B dCtlSlot(A1),D0 ; setup slot # for _SIntRemove
MOVE.L saveSQElPtr(A3),A0 ; get the SQ element pointer
_SIntRemove ; remove the interrupt handler
RTS
Endif
VSCEnableVGuts
If usingTimeMgr Then
Clr.w IntDisableFlag(A3) ; Remember that were enabling things.
MOVE.L A1,-(SP) ; jPrimeTime trashes A1
LEA TTask(A3),A0 ; get time task block in A0
MOVE.L #kVSCVBLTime,D0 ; delay for about 1/60th of a second
MOVE.L jPrimeTime,A1 ; point straight at the Time Manager dispatch vector
JSR (A1) ; start the delay going
MOVEA.L (SP)+,A1 ;
Else
MOVE.L saveSQElPtr(A3),A0 ; get the queue element
LEA VSCBeginIH,A2 ; save Pointer to interrupt handler
MOVE.W #SIQType,SQType(A0) ; setup queue ID
MOVE.L A2,SQAddr(A0) ; setup int routine address
MOVE.L A3,SQParm(A0) ; pass pointer to privates as the parameter
MOVEQ #0,D0 ; parameter size to SInstall is not byte sized so clr whole long
MOVE.B dCtlSlot(A1),D0 ;
_SIntInstall ; and do install
BNE.S @IntBad
move.l a0,-(sp) ; Save the queue element pointer.
Move.l saveAIV3Base(A3),A0 ; Get the AIV3 base address.
Move.b #((1<<7)+(1<<slotVBL)),AIV3SlotEn(A0) ; Enable Slot interrupts.
Move.b #(1<<7)+(1<<AnySlotEn),AIV3IntEn(A0); make sure Any slot interrupts are enabled.
move.l (sp)+,a0 ; restore the queue element pointer.
Endif
CMP.W D0,D0 ; clear z-bit for good result
@IntBad RTS ; return home (if bad, z-bit is set above, so just leave)
ENDWITH
VSCDirectSetEntries
;---------------------------------------------------------------------
;
; Change the CLUT in a direct mode.
;
; A1 = Ptr to AuxDCE
; A2 = Ptr to cs parameter record
; A3 = Ptr to private storage
;
; This routine allows knowledgeable programs modify the contents
; of the CLUT in direct modes (usually for limited color previewing).
; It takes the same parameter block as SetEntries, but SetEntries
; intentionally does not operate when the card is in a direct pixMode.
; This routine takes the same data and operates ONLY when in direct
; modes. It calls the same SetEntries guts as the regular routine.
;
;---------------------------------------------------------------------
BTST #IsDirect,GFlags(A3) ; are we in a direct mode?
BEQ.S VSCCtlBad ; error if not
BRA.S VSCSEGuts ; jump to SetEntries internals if it's OK
VSCSetDefaultMode
;---------------------------------------------------------------------
;
; Write the card default mode into slot pRAM.
;
; A1 = Ptr to AuxDCE
; 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,VSCVidPrivates,SP_Params
;
; Set up a slot parameter block on the stack.
;
SUBA #spBlockSize,SP ; make an slot parameter block on stack
MOVE.L SP,A0 ; get pointer to parm block now
MOVE.B dCtlSlot(A1),spSlot(A0) ; put slot in pBlock
CLR.B spExtDev(A0) ; external device = 0
;
; Read the slot pRAM to determine what the currently saved mode is. The first
; word is the board ID, followed by the default screen depth. Built-in video keeps the video
; sRsrc spID in VendorUse2.
;
SUBA #SizesPRAMRec,SP ; allocate block for pRAM record
MOVE.L SP,spResult(A0) ; point to it
_sReadPRAMRec ; read it
;
; Since PrimaryInit relies on the default mode being set correctly, we check to see that
; the mode to be set is actually valid. Monitors can only see valid sRsrcIDs, so
; it probably wont cause a problem. But we should check it anyway for unsavory
; applications.
;
Move.b csMode(A2),spID(A0) ; Look for the passed in spID.
Clr.l spParamData(A0) ; Clear the fNext flag; we want THIS sRsrc.
Ori.b #(1<<fall)|\ ; Search for both enabled/disabled sRsrcs
(1<<foneslot),spParamData+3(A0) ; Only search in our slot.
_GetsRsrc ; Do it.
Bne @BadExit ; If failed, quit.
Move.w spCategory(A0),D0 ; Get the category.
Cmp.w #catDisplay,D0 ; If its not catDisplay,
Bne @BadExit ; then quit.
Move.w spCType(A0),D0 ; Get the type.
Cmp.w #typVideo,D0 ; If its not typVideo,
Bne @BadExit ; then quit.
Move.w spDrvrSw(A0),D0 ; Get the software kind.
Cmp.w #drSwApple,D0 ; If its not drSwApple,
Bne.s @BadExit ; then quit.
Move.w spDrvrHw(A0),D0 ; Get the hardware ID.
Cmp.w #drHwVSC,D0 ; If its not drHwVSC,
Bne.s @BadExit ; then quit.
;
; 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),D1 ; Copy the desired family mode.
Cmp.b SP_LastConfig(Sp),D1 ; If were already going to this mode,
Beq.s @GoodExit ; then just go on.
Movea.l A0,A1 ; Save the SpBlockPtr.
Move.l #gestaltDisplayMgrAttr,D0 ; We need to know if the Display Manager is around.
_Gestalt ; Ask, and ye shall receive.
Bne.s @NoDM ; Oops, got an error.
Move.l A0,D0 ; Get the result into D0.
Btst #gestaltDisplayMgrPresent,D0 ; If the Display Manager is around,
Bne.s @WriteIt ; then skip the non-dynamic stuff.
@NoDM Bset #spFamilyChanged,SP_Flags(Sp) ; Otherwise, say that were changing the mode.
Cmpi.b #sRsrc_Vid_VSC_FPa,D1 ; If were swapping to the 640x870 mode,
Beq.s @ChkDepth ; then check the depth.
Cmpi.b #sRsrc_Vid_VSC_RGBFPa,D1 ; If were swapping to the 640x870 mode,
Beq.s @ChkDepth ; then check the depth.
Cmpi.b #sRsrc_Vid_VSC_1K,D1 ; If were swapping to the 1024x768 mode,
Beq.s @ChkDepth ; then check the depth.
Cmpi.b #sRsrc_Vid_VSC_FPb,D1 ; If were swapping to the 640x818 mode,
Beq.s @SetDepth ; then set to 8bpp.
Cmpi.b #sRsrc_Vid_VSC_RGBFPb,D1 ; If were swapping to the 640x818 mode,
Beq.s @SetDepth ; then set to 8bpp.
Bra.s @WriteIt ; Otherwise, just go on.
@ChkDepth Cmpi.b #FourthVidMode,SP_Depth(Sp) ; If were not set for 8bpp mode,
Bne.s @WriteIt ; then just go on.
Move.b #ThirdVidMode,SP_Depth(Sp) ; Otherwise, say we want 4bpp instead.
Bra.s @WriteIt ;
@SetDepth Move.b #FourthVidMode,SP_Depth(Sp) ; Use 8bpp.
@WriteIt Movea.l A1,A0 ; Restore the SpBlockPtr.
MOVE.B D1,SP_LastConfig(SP) ; write the mode into pRAM buffer
MOVE.L SP,spsPointer(A0) ; set up parameter block
_sPutPRAMRec ; write the new record out
@GoodExit ADDA #SizesPRAMRec+spBlockSize,SP ; Deallocate buffer and
BRA VSCCtlGood ; return good result.
@BadExit Adda #SizesPRAMRec+spBlockSize,SP ; Deallocate buffer and
Bra VSCCtlBad ; return bad result.
ENDWITH
VSCSwitchMode
;---------------------------------------------------------------------
;
; SwitchMode is called by the Display Manager to either change
; resolutions, bit depth, or both.
;
; A1 = Ptr to AuxDCE
; A2 = Ptr to cs parameter record
; A3 = Ptr to private storage
;
;---------------------------------------------------------------------
With VSCVidPrivates,SpBlock,VDSwitchInfo
; Check to see if we can do what was requested…
;
Tst.w csPage(A2) ; If requested page is not zero,
Bne.s VSCCtlBad ; then we cant help em.
Move.l csData(A2),D2 ; Get the requested SpID.
Moveq #0,D0 ; Clear hi-half of mode/depth register.
Lea SwitchTable,A0 ; Point to the SwitchMode table.
@ChkLoop Move.b (A0)+,D1 ; If were at the end of the table,
Beq VSCCtlBad ; then somethings wrong.
Move.b (A0)+,D0 ; Get the max mode for this config.
Cmp.b D1,D2 ; If this is not the requested mode,
Bne.s @ChkLoop ; then keep looping.
; Make sure the new depth is okay for switching…
;
Move.w csMode(A2),D1 ; Get the requested mode (depth).
Subi.w #FirstVidMode,D1 ; Make it indexed.
Subi.w #FirstVidMode,D0 ; Make the new max mode indexed.
Cmp.w D0,D1 ; If the current mode is > new max mode,
Bgt VSCCtlBad ; then punt.
Move.w D1,D0 ; Save the indexed mode.
; Switch to the new resolution…
;
Cmp.b dCtlSlotID(A1),D2 ; If were already in the requested resolution,
Beq VSCSetVidModeGuts ; then go try the depth switch.
Move.b D2,D1 ; Set up to do the resolution switch.
Bsr VSCSetResolution ; Switch to the new resolution.
Move.w D0,D1 ; Set up to do the depth switch.
Bra VSCSetVidModeGuts ; Switch to the new depth.
Endwith
VSCSetAltSense
;---------------------------------------------------------------------
;
; SetAltSense sets up the alternate senseID pRam byte to contain
; a valid “index” code if a valid sense code is passed
; in csMode (byte 0 is the sense code, byte 1 is the type).
;
; A1 = Ptr to AuxDCE/Ptr to VSCMonIDs table (not restored)
; A2 = Ptr to cs parameter record
; A3 = Ptr to private storage
;
;---------------------------------------------------------------------
With SpBlock,VSCVidPrivates,SP_Params
;
; First, set up a slot parameter block on the stack.
;
Suba.w #spBlockSize,Sp ; Make an SpBlock on the stack.
Move.l Sp,A0 ; Get a pointer to it into A0.
Move.b dCtlSlot(A1),spSlot(A0) ; Set it up.
Clr.b spExtDev(A0)
;
; Next, read the current pRam so that we can set it appropriately…
;
Suba.w #SizesPRAMRec,Sp ; Make an sPRAM block on the stack.
Move.l Sp,spResult(A0) ; Point to it.
_sReadPRamRec ; Get pRAM.
;
; See what we need to do…
;
Moveq #0,D0 ; Clear D0 for good measure.
Move.b csMode+1(A2),D0 ; Get the sense-code type.
Lea VSCMonIDsTbl,A1 ; Point to the MonIDs table.
@TypeLoop Cmp.w (A1)+,D0 ; If weve come to the end of the table,
Bmi.s @MonIDNotValid ; then leave with an error.
Beq.s @ChkOffset ; If weve found a match, then check it.
Tst.w (A1)+ ; Otherwise, skip the offset.
Bra.s @TypeLoop ; And keep looping until were done.
@ChkOffset Move.w (A1)+,D1 ; If the offset to the code table is nil,
Beq.s @MonIDNotValid ; then leave with an error.
Lea VSCMonIDsTbl,A1 ; Otherwise, re-point to the MonIDsTbl.
Adda.w D1,A1 ; And point to the appropriate sense-code table.
Move.b csMode(A2),D0 ; Get the sense code.
@CodeLoop Cmp.b (A1)+,D0 ; If weve come to the end of the table,
Bmi.s @MonIDNotValid ; then leave with an error.
Beq.s @ChkIt ; If weve found a match, then check it.
Tst.b (A1)+ ; Otherwise, skip the indexed code.
Bra.s @CodeLoop ; And keep looping until were done.
@ChkIt Move.b (A1)+,D1 ; Get the indexed ID for this code.
Cmpi.b #indexedNoConnect,D1 ; If its the no-connect code,
Beq.s @ClearIt ; then say so.
Move.b D1,SP_AltSense(Sp) ; Write out “index” to pRam record.
Ori.b #spAltSenseValidMask,SP_AltSense(Sp) ; Validate it.
Bra.s @WritePRam
@ClearIt Clr.b SP_AltSense(Sp) ; Invalidate no-connect byte.
@WritePRam Move.l Sp,spsPointer(A0) ; Set up to whack pRam.
_sPutPRAMRec ; Whack it.
Adda.w #SizesPRAMRec+spBlockSize,Sp ; Restore stack…
Bra VSCCtlGood ; …and leave.
@MonIDNotValid Adda.w #SizesPRAMRec+spBlockSize,Sp ; Restore stack…
Bra VSCCtlBad ; …leave with error.
Endwith
VSCSleepWake
;---------------------------------------------------------------------
;
; SleepWake sets the sleep/wake state of the VSC hardware to the
; the value in csMode.
;
; csMode.b -> 0 = sleep, non-zero = wake
; csMode.w <- [return MonID for Docking Handler]
;
; A1 = Ptr to AuxDCE/Ptr to VSC reg base (not restored)
; A2 = Ptr to cs parameter record
; A3 = Ptr to private storage
;
;---------------------------------------------------------------------
With VSCVidPrivates
Move.b #-1,sleepWakeFlag(A3) ; Say that were sleep-waking.
Tst.b csMode(A2) ; If the request was to sleep, then
Beq.s @ChkSleep ; make sure its okay.
Btst #IsSleeping,GFlags(A3) ; Otherwise, if were already awake, then
Beq VSCCtlBad ; somethings wrong.
Bclr #IsSleeping,GFlags(A3) ; Remember that were no longer asleep.
Moveq #0,D1 ; Set up to power up.
Bra.s VSCLowPwrGuts ; Do it.
@ChkSleep Btst #IsSleeping,GFlags(A3) ; If were already sleeping, then
Bne VSCCtlBad ; somethings wrong.
Move.w saveMonID(A3),csMode(A2) ; Remember which display were driving.
Bset #IsSleeping,GFlags(A3) ; Remember that weve been asked to sleep.
Moveq #-1,D1 ; Set up to power down.
Bra.s VSCLowPwrGuts ; Do it.
VSCLowPwrSelect
;---------------------------------------------------------------------
;
; LowPwrSelect sets the power state of the VSC hardware to the
; the value in csMode.
;
; csMode : 0 = PowerOn VSC, nonzero = PowerDown VSC
;
; A1 = Ptr to AuxDCE/Ptr to VSC reg base (not restored)
; A2 = Ptr to cs parameter record
; A3 = Ptr to private storage
;
;---------------------------------------------------------------------
VSCPSelectRegs REG a0-a1
Lea VSCValidCPUs,A0 ; Point to list of valid CPUs.
Moveq #0,D0 ; Clear D0 for good measure.
@CPULoop Move.w (A0)+,D0 ; Get next entry in list.
Bmi.s VSCCtlBad ; If at EOL, then were dead.
Cmp.b CPUFlag,D0 ; If the entry matches,
Beq.s @EndCPUChk ; then were okay.
Tst.w (A0)+ ; Skip over offset.
Bra.s @CPULoop ; Loop until done.
@EndCPUChk
Clr.b sleepWakeFlag(A3) ; Say that were not sleep-waking.
move.w csMode(a2),d1 ; determine which power state to enter
VSCLowPwrGuts
movem.l VSCPSelectRegs,-(sp) ; save them regs
bne @VSCPowerDown ; IF powering down THEN branch
@VSCPowerUp
tst.b savePowerStatus(a3) ; IF already powered up THEN
beq @VSCPSelectCommonOut ; leave.
bsr VSCPowerOnPlanes ; turn on power planes and clocks
movea.l saveVideoBase(a3),a1 ; get pointer to VSC register base
lea saveParams(a3),a0 ; get pointer to our storage area
move.b (a0)+,VSC_BusInt(a1) ; restore the bus interface
move.b (a0)+,VSC_VidCtrl(a1) ; restore the control register
; enable interrupts
move.l saveAIV3Base(a3),a1 ; Get the AIV3 base address.
If Not usingTimeMgr Then
move.b #((1<<7)+(1<<slotVBL)),\
AIV3SlotEn(a1) ; Enable Slot interrupts.
Endif
move.b #(1<<7)+(1<<AnySlotEn),AIV3IntEn(a1); make sure Any slot interrupts are enabled.
Tst.b sleepWakeFlag(A3) ; If were doing the sleep-wake case,
Bne.s @SkipLowPwr0 ; then skip the low-power stuff.
Bsr VSCRestoreBusErrH ; Otherwise, remove our bus error handler.
If Not usingTimeMgr Then
Bsr VSCRemoveTTask ; Remove phony jVBLTask VBLs.
; ••• bsr VSCEnableVGuts ; power up those interrupts
Endif
Bra.s @SkipGrayScreen0 ; Dont grayscreen on low-power.
@SkipLowPwr0
Move.w saveMode(A3),D1 ; Get the current depth.
Move.w D1,csMode(A2) ; Save it for later.
Subi.w #FirstVidMode,D1 ; Make it an index.
Bsr VSCGrayScreen ; Gray the screen.
@SkipGrayScreen0
move.w #-1,saveMode(A3) ; clobber Mode so that update will take effect
bra.s @VSCPSelectCommonOut ; good bye
;---------------------------------------------------------------------
;---------------------------------------------------------------------
@VSCPowerDown
tst.b savePowerStatus(a3) ; IF already powered down THEN
bne.s @VSCPSelectCommonOut ; exit
movea.l saveVideoBase(a3),a1 ; get pointer to VSC register base
lea saveParams(a3),a0 ; get pointer to our storage area
; save appropriate registers
move.b VSC_BusInt(a1),(a0)+ ; save the bus interface
move.b VSC_VidCtrl(a1),(a0)+ ; save the control register
Tst.b sleepWakeFlag(A3) ; If were doing the sleep-wake case,
Bne.s @SkipLowPwr1 ; then skip the low-power stuff.
bsr VSCInstallBusErrH ; Install our buserr handler
If Not usingTimeMgr Then
; ••• bsr VSCDisableVGuts ; kill interrupts
Bsr VSCInstallTTask ; Enable phony jVBLTask VBLs.
Endif
Bra.s @SkipGrayScreen1 ; Dont grayscreen on low-power.
@SkipLowPwr1
Move.w saveMode(A3),D1 ; Get the current depth.
Subi.w #FirstVidMode,D1 ; Make it an index.
Bsr VSCGrayScreen ; Gray the screen.
@SkipGrayScreen1
bsr VSCPowerDownPlanes ; kill power planes and clocks
@VSCPSelectCommonOut
Movem.l (Sp)+,VSCPSelectRegs ; Restore the work registers.
Tst.b savePowerStatus(A3) ; If were powered down,
Bne VSCCtlGood ; then just leave.
Move.w csMode(A2),D1 ; Set up for sleep-wake just in case.
Tst.b sleepWakeFlag(A3) ; If were doing the sleep-wake case,
Bne VSCSetVidModeGuts ; then restore the depth.
Bra VSCCtlGood ; Vamoose.
Endwith
**********************************************************************
*
* VideoClose releases the device's private storage and removes the
* interrupt handler.
*
*
* Entry: A0 = param block pointer
* A1 = AuxDCE pointer
*
* Other: A2 = temporary AuxDCE pointer copy
*
**********************************************************************
VSCVidClose
WITH VSCVidPrivates
MOVE.L dCtlStorage(A1),A3 ; A3 <- Ptr to private storage
MOVE.L (A3),D0 ;
_StripAddress ;
MOVE.L D0,A3 ;
If usingTimeMgr Then
MOVE.W #-1,IntDisableFlag(A3) ; set this word to “disable” interrupts
LEA TTask(A3),A0 ; get the time manager task block
_RmvTime ; remove this element from the queue
Else
BSR VSCDisableVGuts ; call utility to deactivate interrupts
MOVE.L saveSQElPtr(A3),A0 ; get the slot interrupt queue element ptr
_DisposPtr
Endif
MOVE.L saveGamDispPtr(A3),A0 ; get pointer to gamma table block
_DisposPtr ; and dispose it
Move.l saveVidPtr(A3),A0 ; Get pointer to video parameters block,
_DisposPtr ; and dispose of it.
MOVE.L dCtlStorage(A1),A0 ; dispose of the private storage
_DisposHandle ;
MOVEQ #noErr,D0 ; no error
RTS ; and return
ENDWITH
**********************************************************************
*
* Video Driver Status Call Handler. There are fourteen standard calls:
*
* ($00) Error
* ($01) Error
* ($02) GetMode
* ($03) GetEntries
* ($04) GetPage
* ($05) GetPageBase
* ($06) GetGray
* ($07) GetInterrupt
* ($08) GetGamma
* ($09) GetDefaultMode
* ($0A) GetCurMode
* ($0B) GetSync
* ($0C) GetConnection
* ($0D) GetModeTiming
*
* The following calls are VSC-specific:
*
* ($83) GetAltSense
* ($84) GetPwrSelect
*
* Entry: A0 = paramblock pointer
* A1 = AuxDCE pointer
* Uses: A2 = cs parameters
* A3 = pointer to private storage
* D0-D3 = scratch (don't need to be preserved)
*
* Exit: D0 = error code
*
**********************************************************************
VSCVidStatus
MOVEM.L A0/A1,-(SP) ; Save exit registers.
MOVE.L csParam(A0),A2 ; A2 <- Ptr to control parameters
MOVE.L dCtlStorage(A1),A3 ; A3 <- Ptr to private storage
MOVE.L (A3),D0 ;
_StripAddress ;
MOVE.L D0,A3 ;
MOVE.W csCode(A0),D0 ; get routine selector
Cmpi.w #cscSleepWake,D0 ; If we got the sleep-wake call,
Beq VSCGetSleepWake ; then hop to it.
CMP.W #cscPowerSelect,D0 ; If we got the Power Select
BEQ VSCGetPwrSelect ; do it
TST.B savePowerStatus(a3) ; IF not powered up THEN
BNE.S VSCStatBad ; exit (make no status calls, dammit!)
Cmpi.w #cscAltSense,D0 ; If we got the AltSense call,
Beq VSCGetAltSense ; hop to it.
CMP.W #$0C,D0 ;IF csCode NOT IN [0..C] THEN
BHI.S VSCStatBad ; Error, csCode out of bounds.
MOVE.W VSCStatJumpTbl(PC,D0.W*2),D0 ;Get the relative offset to the routine.
JMP VSCStatJumpTbl(PC,D0.W) ;GOTO the proper routine.
VSCStatJumpTbl
DC.W VSCStatBad-VSCStatJumpTbl ;$00 => Error
DC.W VSCStatBad-VSCStatJumpTbl ;$01 => Error
DC.W VSCGetMode-VSCStatJumpTbl ;$02 => GetMode
DC.W VSCGetEntries-VSCStatJumpTbl ;$03 => GetEntries
DC.W VSCGetPage-VSCStatJumpTbl ;$04 => GetPage
DC.W VSCGetPageBase-VSCStatJumpTbl ;$05 => GetPageBase
DC.W VSCGetGray-VSCStatJumpTbl ;$06 => GetGray
DC.W VSCGetInterrupt-VSCStatJumpTbl ;$07 => GetInterrupt
DC.W VSCGetGamma-VSCStatJumpTbl ;$08 => GetGamma
DC.W VSCGetDefaultMode-VSCStatJumpTbl ;$09 => GetDefaultMode
Dc.w VSCGetCurMode-VSCStatJumpTbl ;$0A => GetCurMode
Dc.w VSCStatBad-VSCStatJumpTbl ;$0B => GetSync (not implemented)
Dc.w VSCGetConnection-VSCStatJumpTbl ;$0C => GetConnection
VSCStatBad MOVEQ #statusErr,D0 ; else say we don't do this one
BRA.S VSCStatDone ; and return
VSCStatGood MOVEQ #noErr,D0 ; return no error
VSCStatDone MOVEM.L (SP)+,A0/A1 ; Restore exit registers.
BRA VSCExitDrvr
VSCGetMode
;---------------------------------------------------------------------
;
; Return the current mode
;
; Inputs : A2 = pointer to csParams
; A3 = pointer to private storage
;
;---------------------------------------------------------------------
WITH VSCVidPrivates
MOVE.W saveMode(A3),csMode(A2) ; return the mode
Clr.w csPage(A2) ; return the page number (always 0)
Move.l saveScreenBase(A3),csBaseAddr(A2) ; Return the screen baseAddr.
BRA.S VSCStatGood ; => return no error
ENDWITH
VSCGetEntries
;---------------------------------------------------------------------
;
; Read the current contents of the CLUT. These values were gamma corrected
; when they were set (by VSCSetEntries), so they may not match the source
; cSpec array.
;
; Inputs : A1 = pointer to AuxDCE
; A2 = pointer to csParams/CLUT read register (not restored to csParams)
; A3 = pointer to private storage
;
;---------------------------------------------------------------------
With VSCCLUTRec
Movem.l D4-D6,-(Sp) ; Save work registers.
Tst.l -(Sp) ; Make some room.
Move.l csTable(A2),D0 ; If we were handed a nil pointer,
Beq @GEErr ; then hike.
_StripAddress ; Make table pointer 32-bit clean.
Move.l D0,(Sp) ; And save it.
Move.w saveMode(A3),D1 ; Get the current video mode.
Sub.w #firstVidMode,D1 ; Convert it to an index.
Moveq #0,D3 ; clear all of D3 for .w compare.
Lea VSCCLUTTbl,A0 ; Point to the table of CLUT data.
Lea (A0,D1*VSCCLUTSize),A0 ; Point to the right entry.
Move.b scRange(A0),D3 ; Get the CLUT range.
Move.w csCount(A2),D4 ; Get the number of entries to change,
Bmi @GEErr ; and hike if its out of range.
Cmp.w D3,D4 ; If D4-D3 > 0 (count - range > 0),
Bhi @GEErr ; then hike.
Tst.w csStart(A2) ; If were doing indexed entries (-1),
Bmi.s @skipStartChk ; then just go on.
Add.w csStart(A2),D4 ; Adjust count for starting position.
Cmp.w D3,D4 ; If D4-D3 > 0 (count - range > 0),
Bhi @GEErr ; then hike.
@skipStartChk
Move.b scStart(A0),D2 ; Get the starting position.
Move.w scSkip(A0),D5 ; Get the inter-entry skip factor.
Move.w csCount(A2),D6 ; Remember the csCount.
Cmpi.w #indexEntries,csStart(A2) ; If table accesses are to be indexed,
Beq.s @GECom ; then go on.
; The following code is BAD, BAD, BAD! We should build our own table here so
; as to NOT mess up the users data. But all the previous Apple video drivers
; have done the same thing here, so well continue the trend for now.
Move.l (Sp),A0 ; Get ptr to csTable.
Move.w D6,D1 ; Get count.
@TableLoop Move.w D4,value(A0,D1*colorSpecSize) ; Write the index into the table.
Subq #1,D4 ; Decrement index.
Dbra D1,@TableLoop ;
@GECom Move.l (Sp)+,A0 ; Get/restore ptr to csTable.
Move.l saveVDACBase(A3),A2 ; Get VDAC base address.
Add.w #ArielDataReg,A2 ; Add offset to Palette read register.
Move.w Sr,-(Sp) ; Save current interrupt level
Bsr VSCWaitVSync ; Wait for VBL.
@Repeat Move.w value(A0),D1 ; Get the NEXT Clut position into D1.
Cmp.w D3,D1 ; If this position is out of range,
Bhi.s @Until ; then go on.
Mulu.w D5,D1 ; Multiply index by skip value.
Add.b D2,D1 ; Add in the starting posistion.
Move.b D1,ArielAddrReg-ArielDataReg(A2) ; Tell the Clut where to read from.
Move.b (A2),D1 ; Get Red:
Move.b D1,rgb+red(A0) ; --> $rrXX
Move.b D1,rgb+red+1(A0) ; --> $rrrr
Move.b (A2),D1 ; Get Green:
Move.b D1,rgb+green(A0) ; --> $ggXX
Move.b D1,rgb+green+1(A0) ; --> $gggg
Move.b (A2),D1 ; Get Blue:
Move.b D1,rgb+blue(A0) ; --> $bbXX
Move.b D1,rgb+blue+1(A0) ; --> $bbbb
@Until Addq #colorSpecSize,A0 ; Point to next entry ColorTable.
Dbra D6,@Repeat
Move.w (Sp)+,Sr ; Restore previous interrupt level.
Movem.l (Sp)+,D4-D6 ; Restore work registers.
Bra VSCStatGood ; Return noError.
@GEErr Tst.l (Sp)+ ; Clean up stack.
Movem.l (Sp)+,D4-D6 ; Restore work registers.
Bra VSCStatBad ; Return statError.
VSCGetPage
;---------------------------------------------------------------------
;
; 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 VSCVidPrivates
MOVE csMode(A2),D1 ; get the mode
MOVE D1,D2 ; keep a copy
BSR VSCChkMode ; is this mode OK?
BNE VSCStatBad ; => not a valid mode
MOVE.W #1,csPage(A2) ; return page count
BRA VSCStatGood ; => return no error
ENDWITH
VSCGetPageBase
;---------------------------------------------------------------------
;
; Return the base address for the specified page in the current mode
;
;---------------------------------------------------------------------
WITH VSCVidPrivates
TST.W csPage(A2) ; are we returning page zero info?
BNE VSCStatBad ; only page 0 is valid
Move.l saveScreenBase(A3),csBaseAddr(A2) ; Return the screen baseAddr.
BRA VSCStatGood ; => return no error
ENDWITH
VSCGetGray
;---------------------------------------------------------------------
;
; Return a boolean, set true if luminance mapping is on
;
;---------------------------------------------------------------------
WITH VSCVidPrivates
MOVEQ #0,D1 ; set up for BFEXTU
VSCGetFlagCom
BFEXTU GFlags(A3){D1:1},D0 ; get the state of flag
MOVE.B D0,csMode(A2) ; return value
BRA VSCStatGood ; => and return
ENDWITH
VSCGetInterrupt
;---------------------------------------------------------------------
;
; Return a boolean in csMode, set true if VBL interrupts are disabled
;
;---------------------------------------------------------------------
WITH VSCVidPrivates
If usingTimeMgr Then
TST.W IntDisableFlag(A3) ; test the interrupt state
BEQ.S @isOn ; if not on,
MOVE.B #1,csMode(A2) ; then return disabled state.
BRA VSCStatGood ;
@isOn
CLR.B csMode(A2) ; return enabled state
BRA VSCStatGood
Else
MOVEQ #1,D1 ; set up BFEXTU to point at IntDisFlag
BRA.S VSCGetFlagCom ; and use common code
EndIf
ENDWITH
VSCGetGamma
;---------------------------------------------------------------------
;
; Return the pointer to the current gamma table
;
;---------------------------------------------------------------------
WITH VSCVidPrivates
MOVE.L saveGammaPtr(A3),csGTable(A2) ; return the pointer to the structure
BRA VSCStatGood ; and return a good result
ENDWITH
VSCGetDefaultMode
;---------------------------------------------------------------------
;
; Read the card default mode from slot pRAM.
;
; A1 = Ptr to AuxDCE
; A2 = Ptr to cs parameter record
; A3 = Ptr to private storage
;
;---------------------------------------------------------------------
WITH spBlock,VSCVidPrivates,SP_Params
;
; Set up a slot parameter block on the stack.
;
SUBA #spBlockSize,SP ; make an slot parameter block on stack
MOVE.L SP,A0 ; get pointer to parm block now
MOVE.B dCtlSlot(A1),spSlot(A0) ; put slot in pBlock
CLR.B spExtDev(A0) ; external device = 0
;
; Read the slot pRAM to determine what the currently saved mode is. The first
; byte is the board ID, followed by the default mode. Built-in video keeps the last
; selected video sRsrc spID in VendorUse2.
;
SUBA #SizesPRAMRec,SP ; allocate block for pRAM record
MOVE.L SP,spResult(A0) ; point to it
_sReadPRAMRec ; read it
MOVE.B SP_LastConfig(SP),csMode(A2) ; return the result
ADDA #SizesPRAMRec+spBlockSize,SP ; release buffer
BRA VSCStatGood ;
ENDWITH
VSCGetCurMode
;---------------------------------------------------------------------
;
; This call will generally be used in conjuntion with the
; the SwitchMode control call. Its function is
; basically to fill out the VDSwitchInfo with the
; current status. Note that, here, csData, is the
; current SlotID (sResource number) in use.
;
; Note: The implementation of this Status call can be used to
; determine whether the SwitchMode control call is
; implemented or currently accessible.
;
; Input: A1 = ptr to AuxDCE
; A2 = ptr to csParams
; A3 = ptr to driver globals
;
;---------------------------------------------------------------------
With VSCVidPrivates,VDSwitchInfo
; Check to see if we even need to be here or not…
;
Moveq #0,D0 ; Clear hi-half of SpID register.
Move.b dCtlSlotID(A1),D0 ; Get the current SpID.
Lea SwitchTable,A0 ; Point to the SwitchMode table.
@ChkLoop Move.b (A0)+,D1 ; If were at the end of the table,
Beq VSCStatBad ; then this mode cant switch.
Cmp.b D0,D1 ; If this is not the current mode,
Bne.s @ChkLoop ; then keep looping.
; Return the appropriate information if all is well…
;
Move.w saveMode(A3),csMode(A2) ; Return the current mode (bit depth).
Move.l D0,csData(A2) ; Return the current SpID.
Move.l saveScreenBase(A3),csBaseAddr(A2) ; Return the base address for this page.
Clr.w csPage(A2) ; But we only support page #0.
Bra VSCStatGood ; Vamoose.
Endwith
VSCGetConnection
;---------------------------------------------------------------------
;
; This call will generally be used in conjuntion with the
; the SwitchMode control call. Its function is
; to fill out the VDDisplayConnectInfo with the
; right status.
;
; Input: A1 = ptr to AuxDCE
; A2 = ptr to csParams
; A3 = ptr to driver globals
;
;---------------------------------------------------------------------
With VSCVidPrivates,VDDisplayConnectInfo
Lea VSCConnectTable,A0 ; Point to the table of connect values.
@ConnectLoop Move.w (A0)+,D0 ; Get the indexed display code.
Bmi.s @UseDefault ; If were at the tables end, then use the defaults.
Cmp.w saveMonID(A3),D0 ; Otherwise, if we found a match, then
Beq.s @FoundOne ; hop to it.
Adda.w #ConnectEntrySz-2,A0 ; Otherwise, skip to the next entry.
Bra.s @ConnectLoop
@UseDefault Lea VSCDfltConnect,A0 ; Point to the default table.
@FoundOne
Move.w (A0)+,csDisplayType(A2) ; Write out the display type.
Move.l (A0)+,csConnectFlags(A2) ; Write out the flags.
Bra VSCStatGood ; Vamoose.
Endwith
VSCGetAltSense
;---------------------------------------------------------------------
;
; Returns the alternate senseID code thats in use. It
; should be noted that we cannot disguish between PAL &
; NTSC monitors & encoder boxes once SetAltSense has
; been called (because both the monitor & box codes are
; mapped into the same indexedSense code). We pas back
; this information in csMode (byte 0 is the sense code,
; byte 1 is the type).
;
; A1 = Ptr to AuxDCE/Ptr to VSCMonIDs table (not restored)
; A2 = Ptr to cs parameter record
; A3 = Ptr to private storage
;---------------------------------------------------------------------
With SpBlock,VSCVidPrivates,SP_Params
;
; First, set up a slot parameter block on the stack.
;
Suba.w #spBlockSize,Sp ; Make a SpBlock on the stack.
Move.l Sp,A0 ; Get a pointer to it into A0.
Move.b dCtlSlot(A1),spSlot(A0) ; Set it up.
Clr.b spExtDev(A0)
;
; Next, read the current pRam so that we can determine whether the
; alternate senseID code is valid or not.
;
Suba.w #SizesPRAMRec,Sp ; Make an sPRAM block on the stack.
Move.l Sp,spResult(A0) ; Point to it.
_sReadPRamRec ; Get pRAM.
Clr.b csMode+1(A2) ; Assume the code is type-0 for now.
Move.b SP_AltSense(Sp),D1 ; Get the alternate senseID byte.
Andi.b #spAltSenseValidMask,D1 ; If it is valid,
Bne.s @Valid ; return it.
Move.b #indexedNoConnect,csMode(A2) ; Otherwise, return the indexed no-connect code,
Bra.s @Exit ; and leave.
@Valid Move.b SP_AltSense(Sp),D1 ; Get the no-connect byte again.
Andi.b #spAltSenseMask,D1 ; Strip the validation code.
Lea VSCType_0_MonIDs,A1 ; Point to the Type_0_MonIDs.
Bsr @ValidLoop ; Search there.
Move.b #6,csMode+1(A2) ; Assume well find a type-6 sense code.
Lea VSCType_6_MonIDs,A1 ; Point to the Type_6_MonIDs.
Bsr @ValidLoop ; Search there.
Move.b #7,csMode+1(A2) ; Assume well find a type-7 sense code.
Lea VSCType_7_MonIDs,A1 ; Point to the Type_7_MonIDs.
Bsr @ValidLoop ; Search there.
Clr.b csMode+1(A2) ; Nothing matched…
Moveq #indexedNoConnect,D0 ; …so just say no-connect.
@WriteIt Move.b D0,csMode(A2) ; Return valid sense code.
@Exit Adda.w #SizesPRAMRec+spBlockSize,Sp ; Restore stack, and
Bra VSCStatGood ; go home.
@ValidLoop Move.b (A1)+,D0 ; Get the sense code.
Cmp.b (A1)+,D1 ; If the index matches whats in PRAM,
Beq.s @WriteItOut ; then return the sense code.
Tst.b (A1) ; If were at the end of the list,
Bmi.s @ChkNextType ; then move on.
Bra.s @ValidLoop ; Otherwise, loop away.
@WriteItOut Addq #4,Sp ; Clean up the stack.
Bra.s @WriteIt ; And leave.
@ChkNextType Rts
Endwith
VSCGetPwrSelect
;---------------------------------------------------------------------
;
; GetPwrSelect returns the power state of the VSC/Jet hardware to the
; the value in csMode. It also returns the powerSelSig in the
; csData field so that we know that this driver supports the
; cscPowerSelect call.
;
; csMode : 0 = PowerOn VSC, nonzero = PowerDown VSC
;
; A1 = Ptr to AuxDCE/Ptr to VSC reg base (not restored)
; A2 = Ptr to cs parameter record
; A3 = Ptr to private storage
;
;---------------------------------------------------------------------
With VSCVidPrivates,VDPageInfo
Lea VSCValidCPUs,A0 ; Point to list of valid CPUs.
Moveq #0,D0 ; Clear D0 for good measure.
@CPULoop Move.w (A0)+,D0 ; Get next entry in list.
Bmi.s VSCStatBad ; If at EOL, then were dead.
Cmp.b CPUFlag,D0 ; If the entry matches,
Beq.s @EndCPUChk ; then were okay.
Tst.w (A0)+ ; Skip over offset.
Bra.s @CPULoop ; Loop until done.
@EndCPUChk
Move.b savePowerStatus(A3),csMode+1(A2) ; Return the current power status.
Move.l #powerSelSig,csData(A2) ; Return the powerSelSig.
Bra VSCStatGood ; Return
Endwith
VSCGetSleepWake
;---------------------------------------------------------------------
;
; GetSleepWake
;
; Returns the current sleep/wake state of the software/hardware.
;
; Input: A1 = ptr to AuxDCE
; A2 = ptr to csParams
; A3 = ptr to driver globals
;
; csParams: csMode.b <- 0 = sleep, non-zero = wake
;
;---------------------------------------------------------------------
With VSCVidPrivates
Btst #IsSleeping,GFlags(A3) ; Test the sleep/wake flag.
Seq csMode(A2) ; Return result.
Move.l #sleepWakeSig,csData(A2) ; Return the sleepWakeSig.
Bra VSCStatGood ; Vamoose.
Endwith
;---------------------------------------------------------------------
;
; Exit from Control or Status.
;
; A0 = Ptr to param block.
; A1 = Ptr to AuxDCE.
; D0 = error code.
;
;---------------------------------------------------------------------
VSCExitDrvr BTST #NoQueueBit,ioTrap(A0) ; no queue bit set?
BEQ.S VSCGoIODone ; => no, not immediate
RTS ; otherwise, it was an immediate call
VSCGoIODone MOVE.L JIODone,-(Sp) ; Get the IODone address,
Rts ; and go there.
;---------------------------------------------------------------------
;
; ChkMode
;
; Verifies the requested mode is legal. Converts spID in D1 into
; zero-based mode number since lots of people want it that way.
;
; <-> D1: Mode
; -> A3: Pointer to driver privates
;
; All registers preserved
;
; Returns EQ if mode is valid.
;
With VSCVidPrivates,VSCVidParams
VSCChkMode
Movem.l A0/D0,-(Sp) ; Save work registers.
Sub.w #FirstVidMode,D1 ; Make mode zero-based.
Blt.s @ModeBad ; If the passed-in mode is < 0,
; then punt.
Move.l saveVidPtr(A3),A0 ; Get a pointer to the video params.
Adda.w #vvpMaxModeBase,A0 ; Point to the base of the max mode values.
Move.b saveSizeVRAM(A3),D0 ; Get the vRAM size index.
Move.b (A0,D0),D0 ; Get the maximum mode for this config.
Sub.w #FirstVidMode,D0 ; Make the max mode zero-based.
Cmp.w D0,D1 ; If the passed-in mode is > max mode,
Bgt.s @ModeBad ; then punt.
Cmp.w D1,D1 ; Set Eq when okay.
@ModeBad Movem.l (Sp)+,A0/D0 ; Restore work registers.
Rts
Endwith
;---------------------------------------------------------------------
;
; Wait for vertical blanking.
;
; A3 = pointer to private storage
;---------------------------------------------------------------------
With VSCVidPrivates
VSCWaitVSync
Tst.b savePowerStatus(A3) ; If were powered down,
Bne.s @NoSyncs ; then just leave.
MOVE.L A0,-(SP) ; Save work registers.
MOVE.L D0,-(SP) ; (Two MOVEs are faster than a MOVEM.)
MOVE.W SR,-(SP) ; Get the status register on stack.
MOVEQ #7,D0 ; Get mask into D0.
AND.B (SP),D0 ; Get the interrupt level.
SUBQ.B #2,D0 ;
BGE.S @OK ; If ≥, then don't change.
ORI.W #$0200,SR ; Raise above level-2.
ANDI.W #$FAFF,SR ; Make it level-2
@OK
Tst.w (Sp)+ ; Restore stack.
If Not usingTimeMgr Then
Move.l saveVideoBase(A3),A0 ; Get the Video Reg base address.
clr.b VSC_IntClear(A0) ; Clear any pending interrupts.
Move.l saveAIV3Base(A3),A0 ; Get the AIV3 base address.
@SyncLoop Btst #slotVBL,AIV3SlotInt(a0) ; If its not pending (0=pending),
Bne.s @SyncLoop ; then keep looping.
Endif
@Done MOVE.L (SP)+,D0 ; Restore work registers.
MOVE.L (SP)+,A0 ; (Two MOVEs are faster than a MOVEM.)
@NoSyncs RTS
Endwith
;---------------------------------------------------------------------
;
; SetDepth sets the depth in the DAC and the framebuffer.
;
; D1 contains the spID of the depth - $80 (the zero based mode ID)
; A1 = AuxDCE POINTER
; A2 = parameter block pointer
; A3 = dCtlStorage pointer
;
; Preserves all registers
;
;---------------------------------------------------------------------
VSCSetDepth
With VSCVidPrivates,VSCVidParams
Movem.l A0-A1/D0-D2,-(Sp) ; Save our work registers.
; Wait for the next VBL before trying to switch depths.
;
Move.w Sr,-(Sp) ; Save the current interrupt level.
Bsr.s VSCWaitVSync ; Wait for the next VBL.
; Switch the framebuffers depth.
;
Move.l saveVideoBase(A3),A0 ; Get the Video Regs base address.
Move.b D1,VSC_Depth(A0) ; Set the depth.
Bsr.s VSCWaitVSync ; Wait for the next VBL.
; Switch the CLUT/DACs depth.
;
Move.l saveVDACBase(A3),A0 ; Point to Ariel.
Move.b ArielConfigReg(A0),d0 ; Save the current state.
And.b #ClrDepthBitsMask,d0 ; Clear the depth bits.
Or.b D1,d0 ; Turn on the depth bits.
Move.b d0,ArielConfigReg(A0) ; Set the depth.
; Go home.
;
Move.w (Sp)+,Sr ; Restore the interrupt level.
Movem.l (Sp)+,A0-A1/D0-D2 ; Restore the work registers.
Rts ; Return to caller.
Endwith
;---------------------------------------------------------------------
;
; SetResolution gets the video driver and the framebuffer controller
; set up for being switched to a different resolution.
;
; D1 containts the spID to the resolution (mode) to enable.
; A1 = AuxDCE POINTER
; A2 = parameter block pointer
; A3 = dCtlStorage pointer
;
; Preserves all registers
;
;---------------------------------------------------------------------
VSCSetResolution
With VSCVidParams,SpBlock
Movem.l D0-D1/A0-A1,-(Sp) ; Save work registers.
Move.b dCtlSlotId(A1),D0 ; Remember which sRsrc to disable.
Move.b D1,dCtlSlotId(A1) ; Remember which sRsrc to enable.
; Do the Slot Manager changes…
;
Suba.w #spBlockSize,Sp ; Allocate a slot parameter block on the stack.
Move.l Sp,A0 ; Point to it with A0.
Move.b dCtlSlot(A1),spSlot(A0) ; Set up the right slot number.
Clr.b spExtDev(A0) ; Dont ask why, just clear this guy.
Move.b D0,spID(A0) ; Write out the spID of the sRsrc.
_sRsrcInfo ; (Update our SpBlock for below.)
Move.l #1,spParamData(A0) ; Say that we want this sRsrc disabled.
_SetSRsrcState ; Do it.
Move.b dCtlSlotId(A1),spID(A0) ; Write out the spID of the sRsrc.
Move.l #0,spParamData(A0) ; Say that we want it enabled.
_SetSRsrcState ; Do it.
_sUpdateSRT ; Tell the Slot Manager to update itself.
; Load the “new” video hardware setup block…
;
Clr.w spID(A0) ; Start looking at spID 0, no external devices.
Clr.b spTBMask(A0) ; Only look for the board sRsrc.
Move.w #catBoard,spCategory(A0) ; Look for: catBoard,
Move.w #typBoard,spCType(A0) ; typBoard,
Clr.w spDrvrSW(A0) ; 0,
Clr.w spDrvrHW(A0) ; 0.
Clr.l spParamData(A0) ; (The board sRsrc must be enabled.)
Bset #foneslot,spParamData+3(A0) ; Limit search to slot 0.
_GetTypeSRsrc ; Get the spsPointer.
Move.b #sVidParmDir,spID(A0) ; Look for the video parameters dir.
_sFindStruct ; Load it.
Move.b dCtlSlotId(A1),spID(A0) ; Look in the directory for this config's parameters.
_sGetBlock ; Load it.
Move.l spResult(A0),-(Sp) ; Save the new privates.
Movea.l saveVidPtr(A3),A0 ; Point to the old privates.
_DisposPtr ; Dispose them.
Move.l (Sp)+,saveVidPtr(A3) ; Start using the new ones.
Adda.w #spBlockSize,Sp ; De-allocate the slot parameter block.
; Change the hardware…
;
Movea.l saveVidPtr(A3),A1 ; Point to the vidParams.
Movea.l saveVideoBase(A3),A0 ; Get the Video Reg base address.
Move.w Sr,-(Sp) ; Save the current interrupt level.
Bsr.s VSCWaitVSync ; Wait for the next VBL.
Bclr #VSCblankBit,VSC_VidCtrl(A0) ; Blank the display.
Bsr VSCSetDotClk ; Set up the dot clock to requested config.
Move.b (A1)+,VSC_HFP(A0) ; Set horizontal front porch.
Move.b (A1)+,VSC_HS(A0) ; Set horizontal sync.
Move.b (A1)+,VSC_HBP(A0) ; Set horizontal back porch.
Move.b (A1)+,VSC_HA(A0) ; Set horizontal active dots.
Move.b (A1)+,VSC_SyncA(A0) ; Set SyncA dots.
Move.w (A1)+,VSC_VFP(A0) ; Set vertical front porch.
Move.w (A1)+,VSC_VS(A0) ; Set vertical sync lines.
Move.w (A1)+,VSC_VBP(A0) ; Set vertical back porch.
Move.w (A1)+,VSC_VA(A0) ; Set vertical active lines.
Bsr.s VSCWaitVSync ; Wait for the next VBL.
Bset #VSCblankBit,VSC_VidCtrl(A0) ; Unblank the display.
; Clean up and go home…
;
Move.w (Sp)+,Sr ; Restore the interrupt level.
Movem.l (Sp)+,D0-D1/A0-A1 ; Restore the work registers.
Rts
Endwith
;---------------------------------------------------------------------
;
; Fill the screen with a 50% dithered gray pattern. To have gotten here
; we must have had a valid display connected, so there are not tests
; for inactive displays here.
;
; D1 = spID of screen depth - FirstVidMode
; A3 = driver private storage
;
; All registers are preserved
;
With VSCVidPrivates,VSCVidParams
VSCGrayScreen
movem.l d0-d6/a1-a2,-(sp) ; Save our work registers.
move.l saveScreenBase(a3),a2 ; Get the base address of the screen.
move.l saveVidPtr(a3),a1 ; Get a pointer to the vidParams.
move.w vvpNumRows(a1),d4 ; Get the number of rows to gray.
adda.w #VVPHdrSize,a1 ; Skip past the header info.
move.w (a1,d1*2),d3 ; Get the number of longwords/row.
move.w d3,d5
andi.w #$03,d5 ; d5 = Rem(bytes/row DIV 4)
lsr.w #2,d3 ; d3 = Int(longs/row)
lea VSCPats,a1 ; Point to the table of gray patterns.
move.l (a1,d1*4),d2 ; Get the gray pattern.
moveq #true32b,d0 ; Set up to flip into 32-bit addressing mode.
_SwapMMUMode ; Do flip.
@nextRow move.l d3,d1 ; Get the number of longswords/row.
move.l d5,d6 ; get remaining bytes/row.
bra.s @cntLong
@nextLong move.l d2,(a2)+ ; Write out long gray to the frame buffer…
@cntLong dbra d1,@nextLong ; …for each scanline.
bra.s @cntByte
@nextByte move.b d2,(a2)+ ; finish remainder of row (if any) with bytes
@cntByte dbra d6,@nextByte
not.l d2 ; Invert the pattern for the next row.
dbra d4,@nextRow ; Repeat for each row.
_SwapMMUMode ; Switch back to the previous addressing mode.
movem.l (sp)+,d0-d6/a1-a2 ; Restore the work registers.
rts ; Return to caller.
Endwith
;---------------------------------------------------------------------
;
; DirectCLUTSet writes gamma-corrected ascending grayscale-like ramps
; into the CLUT
;
; A3 = dCtlStorage pointer
;
; Preserves all registers used.
;
;---------------------------------------------------------------------
With VSCVidPrivates
VSCDirectCLUTSet
MOVEM.L D0-D2/A0/A4-A6,-(SP) ; save registers
MOVE.L saveGammaPtr(A3),A0 ; get pointer to gamma data structure
MOVE.W gFormulaSize(A0),D0 ; get the size of formula data
LEA gFormulaData(A0),A4 ; point to formula data
ADD D0,A4 ; red correction table starts here
MOVE.L A4,A5 ; get default pointer to green data
MOVE.L A4,A6 ; get default pointer to blue data
CMP.W #1,gChanCnt(A0) ; if only only one table, we're set
BEQ.S @OneTbl ; => just one table
MOVE gDataWidth(A0),D1 ; get width of each entry in bits
ADDQ #7,D1 ; round to nearest byte
LSR #3,D1 ; get bytes per entry
MULU gDataCnt(A0),D1 ; get size of table in bytes
ADDA D1,A5 ; calc base of green
ADDA D1,A6 ; calc base…
ADDA D1,A6 ; …of blue
@OneTbl MOVE.W gDataCnt(A0),D2 ; Get number of entries.
Subq #1,D2 ; Make it zero based.
MOVE.L saveVDACBase(A3),A0 ; point to the hardware
ADDA #ArielDataReg,A0 ; point to data register
CLR.B ArielAddrReg-ArielDataReg(A0) ; start at the beginning of CLUT
Move.w #$1F,D2 ; There are only 32 entries to whack in 16bpp mode.
MOVE.W SR,-(SP) ; preserve the status register
BSR VSCWaitVSync ; wait for next blanking period (preserves A0)
; Write an incrementing grayscale ramp.
moveq #0,D0 ; Start the ramp at zero.
@Repeat move.w D0,D1 ; Copy the current index.
bsr.s VSCTrans5to8 ; Translate from 5-bits to 8.
MOVE.B (A4,D1),(A0) ; Write gamma-corrected red.
_CLUTDelay ;
MOVE.B (A5,D1),(A0) ; Write gamma-corrected green
_CLUTDelay ;
MOVE.B (A6,D1),(A0) ; Write gamma-corrected blue.
_CLUTDelay ;
Addq #1,D0 ; Increment the ramp index.
DBRA D2,@Repeat ; Loop until done.
MOVE.W (SP)+,SR ; restore the status reg
MOVEM.L (SP)+,D0-D2/A0/A4-A6 ; restore saved registers
RTS
Endwith
;---------------------------------------------------------------------
;
; Trans5to8
;
; <-> D1: 5-bit value to be converted into an 8-bit index.
;
VSCTrans5to8
Move.l D0,-(Sp) ; Save D0 as scratch.
Moveq #0,D0 ; Clear it.
Move.w D1,D0 ; D1 = ---43210, D0 = ---43210.
Lsl.w #3,D1 ; D1 = 43210---, D0 = ---43210.
Lsr.w #2,D0 ; D1 = 43210---, D0 = -----432.
Or.w D0,D1 ; D1 = 43210432.
Move.l (Sp)+,D0 ; Restore D0.
Rts
;---------------------------------------------------------------------
;
; Misc Utility Routines
;
;---------------------------------------------------------------------
;---------------------------------------------------------------------
; VSCSetSupervisorMode
;
; Input: none
;
; Outputs: none
;
; Destroys: a1/d0
;
; Function: When VM is running we must switch to supervisor mode so
; that we may run necessary privileged instructions. Yes
; we are the operating system and that is acceptable!!!
;---------------------------------------------------------------------
VSCSetSupervisorMode
move.l (sp)+,a1 ; Save a copy of return address since we might switch stacks
moveq #8,d0 ; Set selector to Set Supervisor Mode for VM
_DebugUtil
cmpi.w #paramErr,d0 ; IF VM is on THEN
bne.s @Cont ; D0 = Status Register
move.w sr,d0 ; ELSE Save the current Status Register
@Cont andi.w #$EFFF,sr ; Make sure that we are in the interrupt stack
jmp (a1) ; Get out of here
;---------------------------------------------------------------------
;
; Routine: VSCPowerOnPlanes
;
; Inputs: A3 = Ptr to private storage
;
; Outputs: none
;
; Destroys: none
;
; Function: Turns the power planes on for the video and starts the
; clocks into VSC.
;---------------------------------------------------------------------
WITH VSCVidParams
VSCPowerOnPlanes
VSCPowerOnPlanesReg REG a0-a2/a4/d0
movem.l VSCPowerOnPlanesReg,-(sp) ; save some regs
; Get some useful values up front.
;
movea.l saveAIV3Base(a3),a2 ; get pointer to AIV3
movea.l saveVideoBase(a3),a4 ; get pointer to VSC register base
Movea.l saveVidPtr(A3),A1 ; Point to start of vidParams.
; Make sure everything is off/reset until were ready to go.
;
Bset #VidPwrEn,AIV3PwrEn(A2) ; Turn on video power plane…
Clr.b VSC_VidCtrl(A4) ; …but shut off syncs, dot clock, etc….
Move.w #500-1,D0 ; It takes approximately 500µs…
@Wait500 Tst.b ([VIA]) ; …for the power-on signal to
Dbra D0,@Wait500 ; …propagate thru the video circuitry.
Bset #vidReset,VSC_Test(A4) ; Reset the video subsystem by…
Bclr #vidReset,VSC_Test(A4) ; …toggling the reset bit.
move.b #(1<<slotVBL),AIV3SlotEn(A2) ; Disable VBLs.
; Set up the video for 1bpp.
;
clr.b VSC_Depth(a4) ; Set the frame buffer controller to 1bpp.
bsr VSCSetDotClk ; setup the dot clock to requested config
move.b #((1<<VSCenB0)+(1<<VSCenB1)+\ ; enable both banks of VRAM before we size
(1<<VSCEnDotClk)),VSC_VidCtrl(a4) ; enable the dot clock
Tst.b saveSizeVRAM(A3) ; If weve got 1024K, then
Bne.s @EndSize ; then just go on.
Bclr #VSCEnB1,VSC_VidCtrl(A4) ; Otherwise, configure VSC for only 1 bank.
@EndSize
move.b (a1)+,VSC_HFP(a4) ; set horizontal front porch
move.b (a1)+,VSC_HS(a4) ; set horizontal sync
move.b (a1)+,VSC_HBP(a4) ; set horizontal back porch
move.b (a1)+,VSC_HA(a4) ; set horizontal active dots
move.b (a1)+,VSC_SyncA(a4) ; set SyncA dots
move.w (a1)+,VSC_VFP(a4) ; set vertical front porch
move.w (a1)+,VSC_VS(a4) ; set vertical sync lines
move.w (a1)+,VSC_VBP(a4) ; set vertical back porch
move.w (a1)+,VSC_VA(a4) ; set vertical active lines
; Set up the CLUT/DAC for 1bpp, and then blank it.
;
Movea.l saveVidPtr(A3),A1 ; Point to start of vidParams.
movea.l saveVDACBase(a3),a2 ; Get the base address of the VDAC (Ariel)
move.b #$08,ArielConfigReg(a2) ; Set the CLUT/DAC to 1bpp, master mode, no overlay.
adda.w #ArielDataReg,a2 ; Point to the CLUT/DAC data register.
move.b #$7F,ArielAddrReg-ArielDataReg(a2) ; Setup to write 1st entry.
_ClutDelay
Clr.b (a2) ; Write: red,
_ClutDelay
Clr.b (a2) ; green,
_ClutDelay
Clr.b (a2) ; blue.
_ClutDelay
move.b #$FF,ArielAddrReg-ArielDataReg(a2) ; Setup to write 2nd entry.
_ClutDelay
Clr.b (a2) ; Write: red,
_ClutDelay
Clr.b (a2) ; green,
_ClutDelay
Clr.b (a2) ; blue.
;-------------------------------------------------------
; Turn on video now
;-------------------------------------------------------
; First, turn on the syncs…
;
move.b VSC_VidCtrl(a4),d0 ; Save the current bits.
ori.b #((1<<VSCEnCSync)+\ ; enable composite sync…
(1<<VSCEnVSync)+(1<<VSCEnHSync)+\ ; enable h/v sync…
(1<<VSCExtMuxDelay)),d0 ; enable ???
move.b d0,VSC_VidCtrl(a4) ; Do it!
; Next, wait for the monitor to catch up with us…
;
Move.w Sr,-(Sp) ; Save the status register
Bsr.s VSCWaitVSync ; Wait for next VBL.
Move.w (Sp)+,Sr ; Restore the status register.
; Finally, get video going.
;
Ori.b #(1<<VSCblankBit),VSC_VidCtrl(A4) ; Enable blanking.
@PoweredUp clr.b savePowerStatus(a3) ; set flag to power on state
movem.l (sp)+,VSCPowerOnPlanesReg ; restore them reg
rts
Endwith
;---------------------------------------------------------------------
;
; Routine: VSCSetDotClk
;
; Inputs: a1 - ptr to config word.l/bit count.w
; a3 - VSCVidPrivates
;
;
; Outputs: a1 - bumped past clock info, points to VSC parameters
;
; Destroys: a1
;
; Function: Loads the PLL with a value pointed to by a1. a1+4 = # bits to load
;---------------------------------------------------------------------
With VSCVidParams,VSCVidPrivates
VSCDotClkReg REG a0/a3/d0-d3
VSCSetDotClk
movem.l VSCDotClkReg,-(sp) ; save some regs
move.l VIA,a0 ; VIA base in a0 for delays
move.l saveAIV3Base(a3),a3 ; Ptr to AIV3 base
move.w #firstCtrl,d0 ; first control word
moveq #CtrlCount,d1 ; count
bsr.s VSCSendPLLData ; send it serially
move.l (a1)+,d0 ; serial config word
move.w (a1)+,d1 ; count
bne.s @sendData
move.w #postCtrl,d0 ;if count 0, special case (use default)
moveq #CtrlCount,d1 ; count
bsr.s VSCSendPLLData ; send it serially
bsr VSCDelay8ms ; delay at least 5 ms before sending final word
move.w #postCtrl,d0 ; final control word
moveq #CtrlCount,d1 ; count
bsr.s VSCSendPLLData ; send it serially
bra.s @exit ; and exit
@sendData bsr.s VSCSendPLLData ; send it serially
move.w #postCtrl,d0 ; post control word
moveq #CtrlCount,d1 ; count
bsr.s VSCSendPLLData ; send it serially
bsr VSCDelay8ms ; delay at least 5 ms before sending final word
move.w #finalCtrl,d0 ; final control word
moveq #CtrlCount,d1 ; count
bsr.s VSCSendPLLData ; send it serially
@exit movem.l (sp)+,VSCDotClkReg ; restore them regs
rts ;
Endwith
;---------------------------------------------------------------------
;
; Routine: VSCSendPLLData
;
; Inputs: a3 - Ptr to AIV3 base
; d0.l - Data to shift out
; d1.w - Number of bits to shift out minus 1 (n-1)
;
; Outputs: none
;
; Destroys: d1-d3
;
; Function: Sends the specified data to the PLL through the VIA (AIV3).
;---------------------------------------------------------------------
VSCSendPLLData
move sr,-(sp) ; save the status register
ori #HiIntMask,sr ; mask them interrupts
rol.l #6,d0 ; first, move starting bit into bit 6
@nextBit move.l d0,d3
andi.b #(1<<6),d3 ; Only keep bit 6 (data bit)
move.b AIV3cfg(a3),d2 ; get config reg
andi.b #(1<<0),d2 ; keep CPU speed bit
or.b d3,d2 ; add in data bit
move.b d2,AIV3cfg(a3) ; write the data bit, reset clock line
ori.b #$20,d2
move.b d2,AIV3cfg(a3) ; Set the the clock line
ror.l #1,d0 ; Prep next bit
dbra.w d1,@nextBit
nop
move (sp)+,sr ; restore the status register
rts ;
;---------------------------------------------------------------------
;
; Routine: VSCDelay8ms
;
; Inputs: a0 - Ptr to VIA1 base
;
; Outputs: none
;
; Destroys: d0
;
; Function: Delays around 8 us, for ensuring PLL is programmed correctly.
;---------------------------------------------------------------------
VSCDelay8ms
move.w TimeVIADB,d0
lsl.w #3,d0 ; 8 ms delay
@wait tst.b (a0)
dbra d0,@wait
rts
;---------------------------------------------------------------------
;
; Routine: VSCPowerDownPlanes
;
; Inputs: A3 = Ptr to private storage
;
; Outputs: none
;
; Destroys: none
;
; Function: Turns the power planes off for the video and kills the
; clocks into VSC.
;---------------------------------------------------------------------
With VSCVidPrivates
VSCPowerDownPlanes
move.b #-1,savePowerStatus(a3) ; set flag to power off state
Movea.l saveVideoBase(A3),A0 ; Point to VSC register base.
Movea.l saveAIV3Base(A3),A1 ; Point to AIV3 base address.
Move.w Sr,-(Sp) ; Save the status register.
Bsr VSCWaitVSync ; Wait for the next blanking period.
Clr.b VSC_VidCtrl(A0) ; Blank the display and turn off the dot-clock.
Bclr #VidPwrEn,AIV3PwrEn(A1) ; Now, pull the plug.
Move.w (Sp)+,Sr ; Restore the status register.
Rts ; And hike.
Endwith
;---------------------------------------------------------------------
;
; Routine: VSCInstallBusErrH
;
; Inputs: A2 = Ptr to cs parameter record
; A3 = Ptr to private storage
;
; Outputs: none
;
; Destroys: a0-a1/d0-d1
;
; Function: Install bus error handler so that when screen is
; powered off, framebuffer accesses don't halt the machine
;---------------------------------------------------------------------
VSCInstallBusErrH
bsr VSCSetSupervisorMode ; set unit in supervisor mode
move d0,-(sp) ; save current interrupt level
ori #HiIntMask,sr ; disable interrupts
movec vbr,a0 ; get system current vbr
move.l BusErrVct(a0),saveBusErrVect(a3); save that thing
lea SaveBusErr,a1 ; get pointer to local storage
move.l BusErrVct(a0),(a1) ; save that thing
Lea VSCValidCPUs,A1 ; Point to list of valid CPUs.
Moveq #0,D1 ; Clear D1 for good measure.
@CPULoop Move.w (A1)+,D1 ; Get next entry in list.
Cmp.b CPUFlag,D1 ; If the entry matches,
Beq.s @CalcOffset ; then get offset to handler.
Tst.w (A1)+ ; Skip this offset.
Bra.s @CPULoop ; Loop until match is found.
@CalcOffset Move.w (A1)+,D1 ; Get the offset to appropriate handler.
Lea VSCValidCPUs,A1 ; Point back to list of valid CPUs.
Adda.w D1,A1 ; Get address to handler into A1.
move.l a1,BusErrVct(a0) ; set bus err handler to our own
move (sp)+,sr ; restore interrupt states
rts
;---------------------------------------------------------------------
;
; Routine: VSCRestoreBusErrH
;
; Inputs: A2 = Ptr to cs parameter record
; A3 = Ptr to private storage
;
; Outputs: none
;
; Destroys: a0-a1/d0-d1
;
; Function: Removes bus error handler after screen is power up.
;---------------------------------------------------------------------
VSCRestoreBusErrH
bsr VSCSetSupervisorMode ; set unit in supervisor mode
move d0,-(sp) ; save current interrupt level
ori #HiIntMask,sr ; disable interrupts
movec vbr,a0 ; get system current vbr
move.l saveBusErrVect(a3),BusErrVct(a0); restore the system bus error handler
move (sp)+,sr ; restore interrupt states
rts
;---------------------------------------------------------------------
;
; Routine: VSCBusErrHandler030,VSCBusErrHandler040
;
; Inputs: none
;
; Outputs: none
;
; Destroys: none
;
; Function: bus error handler to prevent framebuffer accesses from halting
; the machine when screen is powered off.
;---------------------------------------------------------------------
VSCStckFrm3 RECORD 0
savereg DS.L 1 ; saved register on stack
;
statusreg DS.W 1 ; [00] status register
programCnt DS.L 1 ; [02] program counter
type DS.B 1 ; [06] format frame type
vectOff DS.B 1 ; [07] vector offset
IntReg DS.W 1 ; [08] Internal Register
ssw DS.W 1 ; [0A] special status register
InstPipe DS.L 1 ; [0C] Instruction Pipe
DFAddr DS.L 1 ; [10] data cycle fault address
IntReg2 DS.L 1 ; [14] more internal registers
DataOBuff DS.L 1 ; [18] data output buffer
IntReg3 DS.L 1 ; [1C] more internal registers
ShortBusSz EQU * ; [1E] size of short stack frame
IntReg4 DS.L 1 ; more internal registers
StageB DS.L 1 ; stage B address
IntReg5 DS.L 1 ; more internal registers
DataIBuf DS.L 1 ; Date Input Buffer
IntReg6 DS.W 3 ; more internal register
vers DS.B 1 ; version #
IntInfo DS.B 1 ; internal information
IntRegs7 DS.W 18 ; more internal register
LongBusSz EQU * ; size of long stact frame
ENDR
VSCStckFrm4 Record 0
savereg DS.L 1 ; saved register on stack
;
statusreg DS.W 1 ; [00] status register
programCnt DS.L 1 ; [02] program counter
type DS.B 1 ; [06] format frame type
vectOff DS.B 1 ; [07] vector offset
effectAddr DS.L 1 ; [08] effective address
ssw DS.W 1 ; [0C] special status register
writeBack3 DS.W 1 ; [0E] write-back 3 status
writeBack2 DS.W 1 ; [10] write-back 2 status
writeBack1 DS.W 1 ; [12] write-back 1 status
DFAddr DS.L 1 ; [14] fault address
wb3Addr DS.L 1 ; [18] write-back 3 address
wb3Data DS.L 1 ; [1C] write-back 3 data
wb2Addr DS.L 1 ; [20] write-back 2 address
wb2Data DS.L 1 ; [24] write-back 2 data
wb1Addr DS.L 1 ; [28] write-back 1 address
wb1Data DS.L 1 ; [2C] write-back 1 data
pdLW1 DS.L 1 ; [30] push data LW 1
pdLW2 DS.L 1 ; [34] push data LW 2
pdLW3 DS.L 1 ; [38] push data LW 3
Endr
VSCBusErrHandler030
WITH VSCStckFrm3
move.l d0,-(sp) ; save working register
move.w ssw(sp),d0 ; get special status register
btst #8,D0 ; IF Not DataFault THEN
beq.s RealBusEx ; can't handle this case - pass it on
move.b DFAddr(sp),D0 ; get high byte of data fault cycle address
cmp.b #$FF,D0 ; IF Slot F THEN
beq.s RealBusEx ; not supported by us
cmp.b #$FE,D0 ; IF less than Slot E THEN
blo.s RealBusEx ; not supported by us
; Have verified that a slot E access caused the bus error. Pop the exception
; stack frame and return.
move.w ssw(sp),d0 ; get special status register
and.w #%1100111011111111,d0 ; FC FB rc rb X X X df RM RW SIZE X FC2 FC1 FC0
move.w d0,ssw(sp) ; set SSW to not rerun cycle
move.l (sp)+,d0 ; restore reg d0
rte
ENDWITH
VSCBusErrHandler040
WITH VSCStckFrm4
move.l d0,-(sp) ; save working register
move.b DFAddr(sp),d0 ; get high byte of data fault cycle address
cmp.b #$FF,d0 ; IF Slot F THEN
beq.s RealBusEx ; not supported by us
cmp.b #$FE,d0 ; IF less than Slot E THEN
blo.s RealBusEx ; not supported by us
; Have verified that a slot E access caused the bus error. Pop the exception
; stack frame and return.
move.w writeBack3(sp),d0 ; get write-back 3 status
andi.w #$F7,d0 ; say its not valid
move.w writeBack2(sp),d0 ; get write-back 2 status
andi.w #$F7,d0 ; say its not valid
move.w writeBack1(sp),d0 ; get write-back 1 status
andi.w #$F7,d0 ; say its not valid
move.l (sp)+,d0 ; restore reg d0
rte
ENDWITH
; The bus exception was not caused by a access to framebuffer - pass the exception to the
; real bus exception handler.
RealBusEx move.l (sp)+,d0 ; restore reg D0
move.l SaveBusErr,-(sp) ; put saved bus exception vector on stack
rts ; jump to bus exception vector
SaveBusErr dc.l 1 ; our system bus err
;--------------------------------------------------------------------- <H6>
; |
; Routine: VSCInstallTTask v
;
; Inputs: A2 = Ptr to cs parameter record
; A3 = Ptr to private storage
;
; Outputs: none
;
; Destroys: a0,d0
;
; Function: Install timer task so that when screen dims, cursor
; movement will be acknowledged.
;---------------------------------------------------------------------
With VSCVidPrivates
VSCInstallTTask
lea VSCTimeMgrIH,a0 ; get a pointer to the interrupt simulation timer task
move.l a0,tmAddr+TTask(a3) ; put it in the time task
lea TTask(a3),a0 ; get a pointer to the timer queue element
move.l #'eada',(a0) ; Put in the magic signature to prevent VM from deferring us.
_InsXTime ; Install the task (fixed frequency).
Clr.w IntDisableFlag(A3) ; Tell the handler to keep the task going.
move.l a1,-(sp) ; jPrimeTime trashes A1
lea TTask(a3),a0 ; get time task block in A0
move.l #kVSCVBLTime,D0 ; delay for about 1/60th of a second
move.l jPrimeTime,a1 ; point straight at the Time Manager dispatch vector
jsr (a1) ; start the delay going
movea.l (sp)+,a1 ;
rts
Endwith
;---------------------------------------------------------------------
;
; Routine: VSCRemoveTTask
;
; Inputs: A2 = Ptr to cs parameter record
; A3 = Ptr to private storage
;
; Outputs: none
;
; Destroys: a0
;
; Function: Remove timer task after dimming.
;---------------------------------------------------------------------
With VSCVidPrivates
VSCRemoveTTask
lea TTask(a3),a0 ; get the time manager task block
_RmvTime
rts
Endwith
;-------------------------------------------------------------
; The Interrupt handler for the VSC Built-In Video
;-------------------------------------------------------------
; If using the Time Manager, on entry A1 points to the TTask block,
; otherwise A1 contains the pointer to the driver's private storage.
VSCTimeMgrIH
MOVE.L A1,-(SP) ; save A1 (it's trashed by JVBLTask)
Moveq #0,D0 ; Clear for byte-to-word access.
Move.b tmXQSize+2(A1),D0 ; get our slot # into D0
MOVE.L JVBLTask,A0 ; call the VBL task manager
JSR (A0) ; with slot # in D0
MOVEA.L (SP)+,A1 ; restore A1 (ptr to TTask block)
TST.W tmXQSize(A1) ; test the flag word to see if “interrupts” are on
; WARNING! - this field must be immediately after the TTask elem
BNE.S @Done ; if ≠ 0, then “interrupts” are disabled, so don't reprime
MOVEA.L A1,A0 ; get time task block addr in A0
MOVE.L #kVSCVBLTime,D0 ; delay for about 1/60th of a second
MOVE.L jPrimeTime,A1 ; point straight at the Time Manager dispatch vector
JSR (A1) ; start the delay going
@Done
RTS ; and return to caller
With VSCVidPrivates
VSCBeginIH
Moveq #0,D0 ; Clear out D0 here for a couple of reasons.
Movea.l saveAIV3Base(A1),A0 ; Get pointer to AIV3 base address.
btst #AnySlot,AIV3Int(a0) ; on-board video interrupting?
bne.s @VBLInt ; if interrupt pending, service it
@notOurs rts ; if not, return from the interrupt
@VBLInt btst #slotVBL,AIV3SlotInt(a0) ; is VBL pending?
bne.s @notOurs ; no, not ours, exit
Move.l A1,-(Sp) ; Save A1 (its trashed by jVBLTask).
Move.b saveSlot(A1),D0 ; Set up for our slot…
Jsr ([jVBLTask]) ; …and call the VBL Task Manager.
Movea.l (Sp)+,A1 ; Restore A1.
Movea.l saveVideoBase(A1),A0 ; Get the Video Reg base address.
clr.b VSC_IntClear(a0) ; Clear any pending interrupts.
@exit moveq #1,D0 ; signal that int was serviced
rts
Endwith