sys7.1-doc-wip/Drivers/Video/TFBDriver.a
2019-07-27 22:37:48 +08:00

1624 lines
62 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: TFBDriver.a
;
; Contains: video driver for TFB card
;
; Written by: Ernie Beernik
;
; Copyright: © 1986-1992 by Apple Computer, Inc. All rights reserved.
;
; Change History (most recent first):
;
; <SM3> 11/3/92 SWC Changed SlotEqu.a->Slots.a and VideoEqu.a->Video.a.
; <SM2> 10/27/92 CSS Changed some short branches to word branches.
; <8> 8/21/91 JSM Clean up header.
; <7> 4/3/91 jmp TFB registers are byte wide, but the TFB reset register was
; being written to as a word. So, I changed a Move.w to a Move.b
; for TFBResetReg. This is coupled with a change in
; TFBDepVideoEqu.a where the TFBResetReg offset was incorrect (+3
; instead of -3).
; <6> 3/22/91 jmp Rolling the current TERROR ROM version of the TFBDriver into
; Reality due to a “screen tearing” problem occurring at
; SetEntries time. I introduced this problem when I was altering
; the TFBDriver for 040 machines. Checked in with GMs approval.
; <3> 2/10/91 jmp Rolled back to the original Reality sources (except for the
; byte-smearing an TERROR-ROM support additions) because of a
; “screen tearing” problem introduced by my screwing around with
; WaitVSync. Oops.
; <2> 1/6/91 jmp Cleaned up conditional assembly stuff (for ease of putting back
; into Reality).
; <1> 11/19/90 jmp Checking into TERROR ROM build for the first time.
; <5> 11/5/90 jmp Renamed DepVideoEqu.a to TFBDepVideoEqu.a; had to change INCLUDE
; name.
; <4> 11/2/90 jmp Most of the changes I made have been fixes for the TFBs
; reliance on byte-smearing, which, of course, doesn't work on
; MC60840 machines. Specifically, I changed the Move.ls in the
; WaitVSync routine to Move.bs. I changed the Move.l in
; TFBSetDepth to Move.b. I simplified the GrayTable routine (i.e.,
; used a bigger DBra rather than 3 Moves per iteration). I removed
; the redundant interrupt-handling code that was occurring both
; before and inside of the calls to WaitVSync.
; <3> 1/4/90 DF Updated interrupt level changing code to reduce vsync wait
; overhead
; <2> 12/28/89 dba made it MAIN instead of PROC so we get dead code stripping
; <1.7> 11/27/89 DAF FOR 6.0.5: Fixed the major bug I created in 1.6. I allowed the
; bit depth rather than the mode spID to get passed to GrayPage.
; This is pretty deadly. This fix corrects the 1.6 bug AND the
; original bug report - that is, you dont need to pass the mode
; spID to the GrayScreen control call. Updated version, too
; <1.6> 11/13/89 DAF For 6.0.5 and greater builds - Corrected some minor TFB driver
; bugs. Corrected GrayPage to use mode and page from registers
; (as it was originally intended). Corrected bug in GetEntries
; when in indexed mode.
; <1.5> 8/28/89 SES Removed references to nFiles.
; <1.4> 8/9/89 DAF Fixed a dereferencing problem in Status/GetPage. Stoopid...
; <1.4> 8/9/89 DAF FOR 6.0.4 BUILD - Fixed a dereferencing problem in
; Status/GetPage
; <1.3> 8/6/89 DAF Added some fixes to slot interrupt handling in the Open, Close,
; and SetInterrupt routines. Also improved error handling in Open
; <1.3> 8/5/89 DAF Updated Open, Close and interrupt routines to improve error and
; slot interrupt handler treatment.
; <1.2> 6/2/89 DAF Corrected dispatch range checking for Control call. Fixed a
; number of register problems in SetInterrupt. Thanks to Jay
; Zipnick for submitting these bugs.
; <1.2> 6/2/89 DAF Fixed a number of bugs in SetInterrupt, corrected control call
; range checking.
; <1.1> 5/2/89 DAF Numerous updates - Linear gamma, improved interrupt handling,
; internal gamma table format is no longer inverted, 3-channel
; gamma table component swap corrected
; <1.1> 5/2/89 DAF Fixed incorrect application of triple gamma tables in 8-bit
; sequence mode. Corrected interrupt disabling to be more friendly
; with F19 and Pink. Added linear gamma support. Updated version
; number. Fixed gamma table so that it's not inverted until used.
; <1.0> 11/16/88 CCH Added to EASE.
; <S461> 4/8/88 DAF Fixed a bug in the close call which caused an extra element to
; be placed in the slot interrupt queue. The bug was benevolent
; but it's better to fix this in the patch.
; <S441> 3/24/88 DAF Fixed GetGamma status call to move a long rather than word (!)
; <S406> 2/26/88 DAF Fixed a register trashing problem in the Status dispatcher
; <S306> 11/23/87 DAF Added SetInterrupt control call, GetGamma and GetInterrupt
; status calls, and updated version number
; <S237> 9/15/87 DAF Changed video driver name back to original
; <S190> 7/1/87 DAF Fixed GetEntries bug, added stack space check in SetEntries
; Corrected GetPageBase to use defMBaseOffset rather than a
; hardcoded value.
; <C843> 2/22/87 RDC Make version field word aligned
; <C827> 2/19/87 EHB Clear color table to gray before switching modes.
; <C839> 2/20/87 GWN Added version field to header.
; <C804> 2/12/87 DAF Added vRAM sizing, returns control error if 8-bit mode requested
; on 256K card.
; <C715> 1/28/87 GWN Made some code review changes.
; 1/25/87 EHB Changed mode table for 32 bit offset into RAM
; 1/21/87 EHB Fixed bug in sequence mode for 4 bits per pixel
; <C648> 1/16/87 EHB Universal TFB Driver for 1K and normal drivers
; <Cxxx> 1/8/87 EHB Added SetGray and GetGray calls for luminance mapping Allocate
; Ptr for gamma Table and store (inverted) gamma in ptr Do
; luminance mapping in SetEntries (index and sequence)
; <Cxxx> 12/26/86 EHB Do new setEntries mode as fast as possible.
; <Cxxx> 12/16/86 EHB Added multiple page support Added control call to clear screen
; <C535> 12/16/86 DAF Added SetGamma call
; <C522> 12/14/86 DAF Added gamma code, new setEntries mode, and shut off interrupts
; during setEntries
; <C497> 12/10/86 GWN Added Toby's code to sync depth change & Set entries. Removed
; DStartOpen flag. Modified Close so the interrupts are disabled
; and SQElement is removed.
; <C457> 11/23/86 EHB Added DStartOpen to header flags
; <C439> 11/18/86 GWN Modified DepthTbl.
; <C420> 11/18/86 RDC Added change to do setup of queue ID for _SIntInstall call
; <C389> 11/10/86 GWN Modified Get/Set Entries for DAF.
; <C371> 11/7/86 RDC Added fix to call jVBLTask instead of jCrsrTask
; <C336> 11/3/86 GWN Changed to new Video parameters as described in video card spec.
; (1,2,4 and 8-bit modes supported). Added mVertRefRate to video
; parameters. Changed to new SDM format header (ByteLanes field).
; Changed to 4-k ROM. Video driver is on ROM. Fixed video driver
; bugs: (1) SetEntries trash A0, (2) SetEntries/GetEntries Data
; NOT.;
; 10/21/86 GWN Changed Get & Set Entries to calculate the proper index for the
; hw dependent CLUT given a hw independent index. (for example:
; <0,1,2,3> => <$00,$40,$80,$C0>
; 10/16/86 GWN CLUT was not being set properly because Index to DepthTbl(D0)
; was calculated incorrectly.
; 10/15/86 GWN Added GetEntries to Get the CLUT.
; 10/13/86 GWN Added SetEntries to set the CLUT.
; 10/8/86 GWN Jump-table is now used for control and status csCode branches
; (CASE csCode OF).
; 10/7/86 GWN Made each module responsible for saving and restoring its local
; registers and data. Status now restores its registers instead of
; via control.
; 10/6/86 GWN 'Cleaned up' comments a bit.
; 10/2/86 GWN Modified Status calls for TFB card (Essentially copied from
; vidroutines.a).
; 9/22/86 GWN Modified control calls for TFB card (Essentially copied from
; vidroutines.a).
; 9/19/86 GWN Added interrupt handler instalation to open (Removed from
; primary init).
;
MACHINE MC68020
LOAD 'StandardEqu.d'
Print Off
INCLUDE 'ColorEqu.a'
INCLUDE 'Slots.a'
INCLUDE 'Video.a'
INCLUDE 'TFBDepVideoEqu.a'
Print On
If ForRom Then
TFBDrvr Proc Export
Else
VideoDrvr Main Export
Endif
;=====================================================================
; Local Vars, definitions, etc....
;=====================================================================
; This is device storage which is stored in the dCtlStorage field of the DCE.
DCEPtr EQU 0 ; pointer to our DCE
saveMode EQU DCEPtr+4 ; the current mode setting
savePage EQU saveMode+2 ; the current page setting
saveBaseAddr EQU savePage+2 ; the current base address
saveSQElPtr EQU saveBaseAddr+4 ; the SQ element pointer (for _SIntRemove). <C497>
GammaPtr EQU saveSQElPtr+4 ; the pointer to the Gamma correction table <C522> DAF
GFlags EQU GammaPtr+4 ; flags word
VRAM256K EQU GFlags+2 ; boolean - TRUE if 256K vidRAM, FALSE if 512K <C804> DAF
dCtlSize EQU VRAM256K+2 ; size of dCtlStorage <C497>
; Flags within GFlags word
GrayFlag EQU 15 ; luminance mapped if GFlags(GrayFlag) = 1
IntDisFlag EQU 14 ; interrupts disabled if GFlags(IntFlag) =1
TFB1K EQU 0
;=====================================================================
; Video Driver Header
;=====================================================================
VidDrvr DC.W $4C00 ; ctl,status,needsLock <C497>
DC.W 0,0,0 ; not an ornament
; Entry point offset table
DC.W VideoOpen-VidDrvr ; open routine
DC.W VidDrvr-VidDrvr ; no prime
DC.W VideoCtl-VidDrvr ; control
DC.W VideoStatus-VidDrvr ; status
DC.W VideoClose-VidDrvr ; close
STRING Pascal
VideoTitle DC.B '.Display_Video_Apple_TFB' ; <PB237/15Sep87> DAF
STRING ASIS
ALIGN ; make sure were aligned <C843>
DC.W CurTFBDrvrVersion ; from TFBDepVideoEqu.a <jmp, 10/16/90>
;
**********************************************************************
*
* VideoOpen allocates private storage for the device in the DCE and locks
* it down for perpituity. Also, install the interrupt handler and enable
* the interrupts. <22Sep86> <22Sep86>
* It also sets the default gamma table included in the driver <C522> DAF
*
* Entry: A0 = param block pointer
* A1 = DCE pointer
*
* Locals: A2 = Saved param block pointer
* A3 = Saved DCE pointer
* A4 = Saved interrupt handler ptr.
*
**********************************************************************
; Save registers
WITH VDPageInfo,SlotIntQElement
VideoOpen MOVE.L A1,A3 ;A3 <- DCE pointer
; Allocate private storage.
MOVEQ #dCtlSize,D0 ; get size of parameters
_ResrvMem ,SYS ; make room as low as possible
MOVEQ #dCtlSize,D0 ; get size of parameters
_NewHandle ,SYS,CLEAR ; get some memory for private storage
BNE OpError ; => return an error in open
MOVE.L A0,dCtlStorage(A3) ; save returned handle in DCE
_HLock ; and lock it down
; get a pointer to private storage in A1 for the rest of Open
MOVE.L (A0),A1 ; <1.3>
; Get and install the interrupt handler. <19Sep86>
LEA BeginIH,A4 ;Save Pointer to interrupt handler. <C715>
MOVEQ #sqHDSize,D0 ;allocate a slot queue element
_NewPtr ,SYS,CLEAR ;get it from system heap cleared
BNE OpError1
MOVE.L A0,saveSQElPtr(A1) ; save pointer for later disposal <1.3>
MOVE.W #SIQType,SQType(A0) ;setup queue ID <C420>
MOVE.L A4,SQAddr(A0) ;setup int routine address
MOVE.L dctlDevBase(A3),SQParm(A0) ;save slot base addr as A3 parm
CLR.L D0
MOVE.B dctlSlot(A3),D0 ;setup slot #
_SIntInstall ;and do install
BNE.S OpError2
; SO DRIVER DOESNT CARRY REDUNDANT GAMMA TABLE, INITIALIZE DRIVER WITH LINEAR GAMMA TABLE.
; Values in the gamma table are no longer inverted in place so that GetGamma will work correctly <1.1>
MOVEQ #0,D0 ; clear high word
MOVE #256+GFormulaData,D0 ; get size of gamma table
_NewPtr ,SYS,CLEAR ; allocate it in system heap
BNE.S OpError3 ; =>failed! return error
MOVE.L A0,GammaPtr(A1) ; else save off pointer
CLR.L (A0)+ ; version = 0, type = 0
MOVEQ #1,D0
MOVE.L D0,(A0)+ ; formula size = 0, channel counte = 1
MOVE.L #$01000008,(A0)+ ; 256 entries, 8 bits per entry
MOVE #$FF,D0 ; get count for dbra loop
@NxtEntry MOVE.B D0,D1 ; copy the index value <1.1>
NOT.B D1 ; invert it <1.1>
MOVE.B D1,(A0)+ ; set next byte <1.1>
DBRA D0,@NxtEntry ; repeat until done
; set luminance mapping and interrupts disabled to false <PB306/23Nov87> DAF
CLR GFlags(A1) ; set all flags false
;
; size video RAM and save boolean in private storage <C804/12Feb87> DAF
;
MOVE.L dctlDevBase(A3),A0 ;get base of vRAM
MOVE.L #TFBTestPos,D1 ;get offset in D1
MOVE.L #TFBTestPat,(A0,D1.L) ;write to alleged RAM
MOVE.L #-1,-(SP) ;write out some garbage to clear data lines
CLR.L (SP)+ ;and pitch it
MOVE.L (A0,D1.L),D0 ;read pattern back
CMP.L #TFBTestPat,D0 ;did it stick?
SNE VRAM256K(A1) ;mark boolean (A1 still has private storage ptr)
; Enable interrupts. <22Sep86>
ADD.L #ClrVInt,A0 ;bump to interrupt reg
CLR.B (A0) ;clear it.
MOVEQ #0,D0 ;no error
EndOpen RTS ;return
;
; Error handlers
;
OpError3 MOVE.L saveSQElPtr(A1),A0 ; get the slot queue element <1.3>
MOVEQ #0,D0 ; clear the register <1.3>
MOVE.B dCtlSlot(A3),D0 ; set the slot <1.3?
_sIntRemove ; remove the interrupt task <1.1>
OpError2 MOVE.L saveSQElPtr(A1),A0 ; get the slot queue block <1.3>
_DisposPtr ; release the slot queue block <1.3>
OpError1 MOVE.L dCtlStorage(A3),A0 ; get the private storage ptr <1.1>
_DisposHandle ; release the private storage <1.1>
OpError MOVE.L #OpenErr,D0 ; say cant open driver <C715>
BRA.S EndOpen
**********************************************************************
*
* VideoClose releases the devices private storage.
*
*
* Entry: A0 = param block pointer
* A1 = DCE pointer
*
**********************************************************************
VideoClose
MOVE.L dCtlDevBase(A1),A0 ;A0 <- base address of device <1.3>
ADD.L #DisableVInt,A0 ;Adjust the base <1.3>
CLR.B (A0) ;Disable interrupt from card <1.3>
MOVE.L dCtlStorage(A1),A2 ;Get pointer to private storage <1.3>
MOVE.L (A2),A2 ; <1.3>
MOVE.L saveSQElPtr(A2),A0 ;Get the SQ element pointer. <1.3>
MOVEQ #0,D0 ; clear out this register <1.2>
MOVE.B dCtlSlot(A1),D0 ;get the slot of the interrupt handler <S461>
_SIntRemove ;Remove the interrupt handler. <C497>
MOVE.L saveSQElPtr(A2),A0 ;dispose this queue element pointer <1.3>
_DisposPtr ; <1.3>
MOVE.L GammaPtr(A2),A0 ;get pointer to gamma table
_DisposPtr ;and dispose it
MOVE.L dCtlStorage(A1),A0 ;Dispose of the private storage. <C497>
_DisposHandle ; <C497>
MOVEQ #0,D0 ;get error into D0 <C715>
RTS ;return to caller
;
**********************************************************************
*
* Video Driver Control Call Handler. Right now there are six calls:
*
* (0) Reset (VAR mode, page: INTEGER; VAR BaseAddr: Ptr);
* (1) KillIO
* (2) SetMode(mode, page: INTEGER; VAR BaseAddr: Ptr);
* (3) SetEntries ( Table: Ptr; Start,Count : integer ); <C522/14Dec86> DAF
* (4) SetGamma ( Table : Ptr ); <C522/14Dec86> DAF
* (5) GrayPage (page); <EHB12/16>
* (6) SetGray (csMode = 0 for color, 1 for gray)
* (7) SetInterrupt (csMode = 0 to disable, 1 to enable) <S306/23Nov87> DAF
*
* Entry: A0 = param block pointer
* A1 = DCE pointer
* Uses: A2 = cs parameters (ie. A2 <- csParam(A0)) (must be preserved)
* A3 = scratch (doesnt need to be preserved)
* A4 = scratch (must be preserved)
* D0-D3 = scratch (dont need to be preserved)
*
* Exit: D0 = error code
*
**********************************************************************
; Decode the call
VideoCtl MOVEM.L A0/A4/D4,-(SP) ; save work registers (A0 is saved because it is used by ExitDrvr).
MOVE.W csCode(A0),D0 ; get the opCode
MOVE.L csParam(A0),A2 ; A2 <- Ptr to control parameters <22Sep86>
CMP.W #7,D0 ;IF csCode NOT IN [0..7] THEN <S306/23Nov87> DAF
BHI.S CtlBad ; Error, csCode out of bounds.
LSL.W #1,D0 ;Adjust csCode to be an index into the table.
MOVE.W CtlJumpTbl(PC,D0.W),D0 ;Get the relative offset to the routine.
JMP CtlJumpTbl(PC,D0.W) ;GOTO the proper routine.
CtlJumpTbl DC.W VidReset-CtlJumpTbl ;$00 => VidReset
DC.W CtlGood-CtlJumpTbl ;$01 => CtlGood
DC.W SetVidMode-CtlJumpTbl ;$02 => SetVidMode
DC.W SetEntries-CtlJumpTbl ;$03 => SetEntries
DC.W SetGamma-CtlJumpTbl ;$04 => SetGamma <C522/14Dec86> DAF
DC.W GrayPage-CtlJumpTbl ;$05 => GrayPage <EHB12/16>
DC.W SetGray-CtlJumpTbl ;$06 => SetGray
DC.W SetInterrupt-CtlJumpTbl ;$07 => SetInterrupt <S306/22Nov87> DAF
SENoMem ADDQ #4,SP ; fix up the stack <S190> DAF
CtlBad MOVEQ #controlErr,D0 ; else say we dont do this one
BRA.S CtlDone ; and return
CtlGood MOVEQ #noErr,D0 ; return no error
CtlDone MOVEM.L (SP)+,A0/A4/D4 ; restore registers.
BRA ExitDrvr
VidReset
;---------------------------------------------------------------------
;
; Reset the card to its default (one bit per pixel)
;
;---------------------------------------------------------------------
BSR TFBInit ; initialize the card <EHB12/16>
MOVE #OneBitMode,csMode(A2) ; return default mode <EHB12/16>
MOVE #1,D1 ; get depth in D1 <EHB12/16>
MOVEQ #0,D0 ; get page in D0 <EHB12/16>
MOVE D0,csPage(A2) ; return the page <EHB12/16>
MOVE.L dCtlStorage(A1),A3 ; get handle to our data <EHB12/16>
MOVE.L (A3),A3 ; A3 = our data <EHB12/16>
BSR TFBSetDepth ; set the depth from D1 <EHB12/16>
BSR TFBSetPage ; set the page from D0 <EHB12/16>
MOVE.L saveBaseAddr(A3),csBaseAddr(A2) ; return the base address <EHB12/16>
BSR GrayScreen ; paint the screen gray <EHB12/16>
BRA.S CtlGood ; => no error <EHB12/16>
SetVidMode
;---------------------------------------------------------------------
;
; Set the card to the specified mode and page.
; If either is invalid, returns badMode error.
;
; If the card is already set to the specified mode, then do nothing.
;
; Note: Mode set is [1,2,4,8].
;
;---------------------------------------------------------------------
MOVE.W csMode(A2),D1 ; D1 = mode
BSR ChkMode ; get mode, check, map to depth (1, 2, 4 or 8) {D1 <- depth}
BNE.S CtlBad ; => not a valid mode
MOVE.W csPage(A2),D0 ; D0 = page
BSR ChkPage ; check page <EHB12/16>
BNE.S CtlBad ; => not a valid page <EHB12/16>
; Only set the mode if it has changed
; TFBSetDepth and TFBSetPage update the saved data in the dCtlStorage
SetEm
MOVE.L dCtlStorage(A1),A3 ; get handle to our data
MOVE.L (A3),A3 ; A3 = our data
MOVE.W csMode(A2),D2 ; D2 = mode <EHB12/17>
CMP saveMode(A3),D2 ; has the mode changed? <EHB12/17>
BEQ.S ModeOK1 ; => no, check the page <EHB12/16>
BSR.S GrayTable ; set color table to gray <C827>
BSR TFBSetDepth ; set the depth, get rowbytes <EHB12/16>
BSR TFBSetPage ; set the page <EHB12/16>
BRA.S NoChange ; => and return
ModeOK1 BSR TFBSetPage ; set the page <EHB12/16>
NoChange MOVE.L saveBaseAddr(A3),csBaseAddr(A2) ; return the base address <EHB12/16>
BRA.S CtlGood ; => return no error
GrayTable ; new routine <C827>
;---------------------------------------------------------------------
;
; Jam the entire color table to gray before switching modes.
;
;---------------------------------------------------------------------
MOVEM.L A0-A1/D0-D1,-(SP) ; save registers
MOVE.W SR,-(SP) ; preserve the status register <2>
MOVEQ #7,D0 ; get mask in D0 <2>
AND.B (SP),D0 ; get the interrupt level <2>
SUBQ.B #2,D0 ; <2>
BGE.S @OK ; if ≥, then don't change <2>
ORI.W #$0200,SR ; raise above level-2 <2>
ANDI.W #$FAFF,SR ; make it level-2 <2>
@OK
BSR WaitVSync ; wait for next blanking period
MOVE.L dCtlStorage(A1),A0 ; get handle to private storage
MOVE.L (A0),A0 ; get pointer to storage (its locked!)
MOVE.L GammaPtr(A0),A0 ; get pointer to gamma data structure
MOVE GFormulaSize(A0),D0 ; get the size of formula data
LEA GFormulaData(A0),A0 ; point to formula data
ADD D0,A0 ; first correction table starts here
MOVE #$80,D1 ; get value for medium gray
MOVE.B (A0,D1),D1 ; get gamma corrected gray <1.1>
NOT.B D1 ; invert it <1.1>
MOVE.L dCtlDevBase(A1),A0 ; A0 <- base address of device.
ADD.L #ClrTbl+wCLUTDataReg,A0 ; add offset to color table data register
MOVE #$FF,D0 ; get count
@Repeat MOVE.B D1,(A0) ; PUT RED COMPONENT
MOVE.B D1,(A0) ; PUT GREEN COMPONENT
MOVE.B D1,(A0) ; PUT BLUE COMPONENT
DBRA D0,@Repeat ; UNTIL (entire table has been copied)
MOVE (SP)+,SR ; restore the status reg
MOVEM.L (SP)+,A0-A1/D0-D1 ; restore saved registers
RTS
SetEntries
;---------------------------------------------------------------------
;
; Input :
; csParam -> datablock
; datablock = csTable -> table of colorSpecs (not colortable)
; csStart -> where to start setting, or -1
; csCount -> # of entries to change
;
; This call has two modes. In SEQUENCE mode, csCount entries are changed
; in the CLUT, starting at csStart. In INDEX mode, csCount entries are
; installed into the CLUT at the positions specified by their .value fields.
; This mode is selected by passing csStart = -1. In both cases, entries are
; range-checked to the dynamic range of the video mode (bits/pixel).
;
;---------------------------------------------------------------------
;
; Set the CLUT.
; A0 = Ptr to the table.
; A1 = Ptr to DCE.
; A2 = Ptr to cs parameter record.
; A3 = Ptr to Vert sync state or Ptr to CLUT
; A4 = Ptr to gamma red table
; A5 = Ptr to gamma green table
; A6 = Ptr to gamma blue table
;
; D0-D4 = Scratch
; D5 = GRAY FLAG
; D6 = Index range [0..n].
; D7 = Shift constant (7,6,4 or 0).
;
;---------------------------------------------------------------------
; Initialize loop.
MOVE.L csTable(A2),D0 ; Check for a nil pointer.
BEQ CtlBad
MOVEM.L A5-A6/D5-D7,-(SP) ; save registers for gamma (A4/D4 saved by VideoCtl) <C522> DAF
MOVE.L dCtlStorage(A1),A0 ; get handle to private storage <C522> DAF
MOVE.L (A0),A0 ; get pointer to storage (its locked!) <C522> DAF
MOVE GFLAGS(A0),D5 ; KEEP FLAGS WORD IN D5
MOVE.L GammaPtr(A0),A0 ; get pointer to gamma data structure <C522> DAF
MOVE.W GFormulaSize(A0),D0 ; get the size of formula data <C522> DAF
LEA GFormulaData(A0),A4 ; point to formula data <C522> DAF
ADD D0,A4 ; red correction table starts here <C522> DAF
MOVE.L A4,A5 ; get default pointer to green data <C522> DAF
MOVE.L A4,A6 ; get default pointer to blue data <C522> DAF
CMP #1,GChanCnt(A0) ; if only only one table, were set <C522> DAF
BEQ.S OneTbl ; => just one table <C522> DAF
MOVE GDataCnt(A0),D0 ; get # entries in table <C522> DAF
MOVE GDataWidth(A0),D1 ; get width of each entry in bits
ADD #7,D1 ; round to nearest byte
LSR #3,D1 ; get bytes per entry
MULU D1,D0 ; get size of table in bytes
ADDA D0,A5 ; calc base of green <C522> DAF
ADDA D0,A6 ; calc base of blue <C522> DAF
ADDA D0,A6 ; calc base of blue <C522> DAF
OneTbl SUB #16*8,SP ; make room for 16 entries <EHB>
; Get the index range (D6) and the shift constant (D7).
BSR CvtIndex
MOVE.L csTable(A2),A0 ; get colorSpec pointer in A0 <EHB>
; If it is sequence mode, then go do fast way
MOVE.W csStart(A2),D1 ; D1 = mode/start <C522> DAF
BMI.S NoSequence ; => not sequential <C522> DAF
CMP #255,D6 ; doing 256 entries?
BNE.S SLOSEQUENCE ; =>NO, NOT 8 BIT MODE <CXXX> EHB
CMP #4,csCount(A2) ; MORE THAN 4 ENTRIES? <CXXX> EHB
BGT SEQUENCE ; =>yes, fast special case
; For 4-bit and less, set up a color table on the stack that has indices
SloSequence MOVE.L SP,A3 ; point to stackSpecs <EHB>
MOVE csCount(A2),D0 ; get elements to copy <EHB>
@NxtSpec MOVE D1,(A3)+ ; copy index into stackSpec <EHB>
MOVE.L (A0)+,D2 ; get index, red <EHB>
MOVE D2,(A3)+ ; copy red <EHB>
MOVE.L (A0)+,(A3)+ ; copy green, blue <EHB>
ADDQ #1,D1 ; bump to next index <EHB>
DBRA D0,@NxtSpec ; => repeat for all specs <EHB>
MOVE.L SP,A0 ; get colorSpec pointer in A0 <EHB>
; disable cursor interrupts so that there will be time to change colors
; A0 already contains the pointer to colorSpecs
NoSequence
MOVE.W SR,-(SP) ; preserve status register <2>
MOVEQ #7,D0 ; get mask in D0 <2>
AND.B (SP),D0 ; get the interrupt level <2>
SUBQ.B #2,D0 ; <2>
BGE.S @OK ; if ≥, then don't change <2>
ORI.W #$0200,SR ; raise above level-2 <2>
ANDI.W #$FAFF,SR ; make it level-2 <2>
@OK
BSR WaitVSync ; wait for next blanking period (preserves A0)
; Copy the table to the CLUT
MOVE.W csCount(A2),D0 ; get the number of elements to change <C522> DAF
MOVE.L dCtlDevBase(A1),A3 ; A3 <- base address of device.
ADD.L #ClrTbl+wCLUTDataReg,A3 ; Add offset to color table data register <C522> DAF
@Repeat MOVE.L (A0)+,D2 ; GET INDEX, RED
SWAP D2 ; GET INDEX
MOVEQ #-1,D1 ; SET HIGH BITS FOR ROL.L
MOVE D2,D1 ; AND MOVE INTO D1
CMP.W D6,D1 ; IF D1 > MAXINDEX THEN
BHI.S @Until ; INDEX OUT OF RANGE
ROL.L D7,D1 ; CONVERT INDEX TO CLUT ENTRY
MOVE.B D1,wCLUTAddReg-wCLUTDataReg(A3) ; POINT TO CLUT ENTRY
SWAP D2 ; LEAVE RED IN D2
MOVE.L (A0)+,D3 ; GET GREEN,BLUE
MOVE.L D3,D4 ; GET BLUE IN D4.W
SWAP D3 ; GET GREEN IN D3.W
TST D5 ; TEST FLAGS WORD
BPL.S @NOGRAY ; =>DONT DO LUMINANCE MAPPING
MULU #$4CCC,D2 ; MULTIPLY BY WEIGHT FOR RED
MULU #$970A,D3 ; MULTIPLY BY WEIGHT FOR GREEN
MULU #$1C28,D4 ; MULTIPLY BY WEIGHT FOR BLUE
ADD.L D3,D2 ; GET SUM OF RED, GREEN
ADD.L D4,D2 ; AND BLUE INTO D2
ROL.L #8,D2 ; GET HIGH BYTE AS LUMINANCE
AND #$00FF,D2 ; MAKE D2 A BYTE INDEX
MOVE.B (A4,D2),D2 ; get gamma corrected gray from red table <1.1>
NOT.B D2 ; invert it for hardware <1.1>
MOVE.B D2,(A3) ; PUT RED COMPONENT
MOVE.B D2,(A3) ; PUT GREEN COMPONENT
MOVE.B D2,(A3) ; PUT BLUE COMPONENT
DBRA D0,@Repeat ; UNTIL (entire table has been copied)
BRA.S @NOMORE ; => RETURN
@NOGRAY ROR #8,D2 ; GET HIGH BYTE OF RED
MOVEQ #0,D1 ; CLEAR OUT INDEX
MOVE.B D2,D1 ; AND GET BYTE INDEX
MOVE.B (A4,D1),D1 ; get gamma corrected red <1.1>
NOT.B D1 ; invert it for hardware <1.1>
MOVE.B D1,(A3) ; AND PUT TO CLUT
ROR #8,D3 ; GET HIGH BYTE OF GREEN
MOVEQ #0,D1 ; CLEAR OUT INDEX
MOVE.B D3,D1 ; AND GET BYTE INDEX
MOVE.B (A5,D1),D1 ; get gamma corrected green <1.1>
NOT.B D1 ; invert it for hardware <1.1>
MOVE.B D1,(A3) ; AND PUT TO CLUT
ROR #8,D4 ; GET HIGH BYTE OF BLUE
MOVEQ #0,D1 ; CLEAR OUT INDEX
MOVE.B D4,D1 ; AND GET BYTE INDEX
MOVE.B (A6,D1),D1 ; get gamma corrected blue <1.1>
NOT.B D1 ; invert it for hardware <1.1>
MOVE.B D1,(A3) ; AND PUT TO CLUT
@Until DBRA D0,@Repeat ; UNTIL (entire table has been copied)
@NOMORE MOVE.W (SP)+,SR ; restore the status reg <C522> DAF
ADD #16*8,SP ; strip stackSpecs
MOVEM.L (SP)+,A5/A6/D5-D7 ; restore saved registers <C522> DAF
BRA CtlGood ; => return no error
SEQUENCE
; make sure the stack is long aligned, and allocate buffer on stack
MOVE.L SP,D1 ; get stack
MOVE.L D1,D0 ; copy stack
NEG D0 ; get amount to subtract
AND #$3,D0 ; get low bits
BEQ.S StkOk ; => already long aligned
SUB D0,SP ; else make stack long aligned
StkOk MOVE.L D1,-(SP) ; save original stack on stack
_StackSpace ; how much room is left on stack? <S190> DAF
CMP.L #$400+$100,D0 ; enough for 1024+256 slop? <S190> DAF
BLT SENoMem ; sorry, not enough <S190> DAF
SUB #$400,SP ; make room for 256*4 bytes
; build the color table on the stack
MOVE csStart(A2),D1 ; get the starting element
LEA 0(SP,D1*4),A3 ; A3 = pointer to first stack element
MOVE.W csCount(A2),D0 ; get the number of elements to change
MOVE.L csTable(A2),A0 ; get a pointer to the table of colorspecs
@Repeat CMP D6,D1 ; check index against max
BGT.S NoMore ; => oops, table is full
TST D5 ; TEST FLAGS WORD
BPL.S @NOGRAY ; =>DONT DO LUMINANCE MAPPING
MOVE.L (A0)+,D2 ; GET RED IN D2.W
MOVE.W (A0)+,D3 ; GET GREEN IN D3.W
MOVE.W (A0)+,D4 ; GET BLUE IN D4.W
MULU #$4CCC,D2 ; MULTIPLY BY WEIGHT FOR RED
MULU #$970A,D3 ; MULTIPLY BY WEIGHT FOR GREEN
MULU #$1C28,D4 ; MULTIPLY BY WEIGHT FOR BLUE
ADD.L D3,D2 ; GET SUM OF RED, GREEN
ADD.L D4,D2 ; AND BLUE INTO D2
ROL.L #8,D2 ; GET HIGH BYTE AS LUMINANCE
MOVEQ #0,D3 ; CLEAR HIGH PART
MOVE.B D2,D3 ; copy the luminence
MOVE.B (A4,D3),D2 ; GET GAMMA CORRECTED GRAY
MOVE.B D2,D3 ; GET THE CORRECTED BLUE
LSL.L #8,D3 ; D3 = 00B0
MOVE.B D2,D3 ; D3 = 00BG
LSL.L #8,D3 ; D3 = 0BG0
MOVE.B D2,D3 ; D3 = 0BGR
MOVE.L D3,(A3)+ ; put blue, green, red to stack
ADDQ #1,D1 ; bump index
DBRA D0,@Repeat ; UNTIL (entire table has been copied)
BRA.S NOMORE ; => RETURN
@NOGRAY MOVEQ #0,D2 ; clear high part
MOVE.B rgb+blue(A0),D2 ; get blue
MOVE.B (A6,D2),D3 ; get the gamma corrected blue <1.1>
LSL.L #8,D3 ; D3 = xxBx
MOVE.B rgb+green(A0),D2 ; get green
MOVE.B (A5,D2),D3 ; get the gamma corrected green
LSL.L #8,D3 ; D3 = xBGx
MOVE.B rgb+red(A0),D2 ; get red
MOVE.B (A4,D2),D3 ; get the gamma corrected red (D3 = xBGR) <1.1>
MOVE.L D3,(A3)+ ; put blue, green, red to stack
ADDQ #1,D1 ; bump index
ADDQ #8,A0 ; point to next color spec
@Until DBRA D0,@Repeat ; UNTIL (entire table has been copied)
NoMore
MOVE.W SR,-(SP) ; preserve the status register <2>
MOVEQ #7,D0 ; get mask in D0 <2>
AND.B (SP),D0 ; get the interrupt level <2>
SUBQ.B #2,D0 ; <2>
BGE.S @OK ; if ≥, then don't change <2>
ORI.W #$0200,SR ; raise above level-2 <2>
ANDI.W #$FAFF,SR ; make it level-2 <2>
@OK
MOVE.L A3,A0 ; save pointer to last element inserted
BSR WaitVSync ; wait for vertical blanking
MOVE.L dCtlDevBase(A1),A3 ; A3 <- base address of device
ADD.L #ClrTbl,A3 ; bump to first control register
MOVEQ #0,D1 ; clear out D1
MOVE.B csStart+1(A2),D1 ; get the starting element to change
NOT.B D1 ; negate for position of starting element
MOVE.W csCount(A2),D0 ; get the number of elements to change
SUB.W D0,D1 ; calc first element to change
NOT.B D1 ; and negate for nubus
MOVE.B D1,wCLUTAddReg(A3) ; set address of first entry
ADD.L #wCLUTDataReg,A3 ; get address of data register
NextRGB MOVE.L -(A0),D1 ; get B,G,R
NOT.L D1 ; invert all three channels for hardware <1.1>
MOVE.B D1,(A3) ; set red
LSR.L #8,D1 ; get green
MOVE.B D1,(A3) ; set green
LSR.L #8,D1 ; get blue
MOVE.B D1,(A3) ; set green
DBRA D0,NextRGB ; => repeat for next RGB
MOVE.W (SP)+,SR ; restore the status reg <C522> DAF
ADD #$400,SP ; strip stack frame
MOVE.L (SP)+,SP ; restore stack alignment
ADD #16*8,SP ;
MOVEM.L (SP)+,A5/A6/D5-D7 ; restore saved registers <C522> DAF
BRA CtlGood ; => return no error
SetGamma ; <C522/15Dec86> DAF
;---------------------------------------------------------------------
;
; Set the gamma table
; A0 = Ptr to private storage
; A1 = Ptr to DCE
; A2 = Ptr to cs parameter record
;
; If the csGTable parameter is NIL, then set the gamma table to be a linear
; gamma table. <1.1>
;
;---------------------------------------------------------------------
; get new gamma table and check that we know how to handle it
MOVE.L A1,-(SP) ; save DCE pointer
MOVE.L dCtlStorage(A1),A3 ; get handle to private storage
MOVE.L (A3),A3 ; get pointer to storage
MOVE.L csGTable(A2),D0 ; test for a NIL pointer
BEQ LinearTab ; if so, then this set up linear gamma
MOVE.L D0,A1 ; get pointer to new gamma table
TST.L GVersion(A1) ; version, type = 0?
BNE BadCtl ; => no, return error <SM2> CSS
CMP #8,GDataWidth(A1) ; is data width 8?
BNE BadCtl ; => no, return error <SM2> CSS
CMP #256,GDataCnt(A1) ; 256 values per channel?
BNE BadCtl ; => no, return error <SM2> CSS
; if new table is different size, reallocate memory
MOVE.L GammaPtr(A3),A0 ; get current gamma in A0
MOVE GFormulaSize(A1),D0 ; get size of formula in new
CMP GFormulaSize(A0),D0 ; same as current gamma table
BNE.S @GetNew ; =>no, resize pointer
MOVE GChanCnt(A1),D0 ; get number of tables in new
CMP GChanCnt(A0),D0 ; same as current gamma table?
BEQ.S @SizeOK ; => yes, data size ok
BGT.S @GetNew ; => new one is bigger, save old one
@NewSize _DisposPtr ; if new one smaller, dispose old one
CLR.L GammaPtr(A3) ; flag its been disposed
@GetNew MOVE #256,D0 ; get number of entries
MULU GChanCnt(A1),D0 ; multiply by number of tables
ADD GFormulaSize(A1),D0 ; add size of formula data
ADD #GFormulaData,D0 ; add gamma table header size
_NewPtr ,Sys ; and allocate a new pointer
BNE.S BadCtl ; => unable to allocate storage
MOVE.L GammaPtr(A3),D0 ; get old gamma table
MOVE.L A0,GammaPtr(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 GammaPtr(A3),A0 ; get new gamma table back
; copy the gamma table header
@SizeOK MOVE GChanCnt(A1),D0 ; get number of tables
MOVE GFormulaSize(A1),D1 ; get size of formula data
MOVE.L (A1)+,(A0)+ ; copy gamma header
MOVE.L (A1)+,(A0)+ ; which is
MOVE.L (A1)+,(A0)+ ; 12 bytes long ***
; copy the data
MOVE #256,D2 ; get number of entries
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 (A1)+,(A0)+ ; move a byte <1.1>
DBRA D2,@NxtByte ; => repeat for all bytes
Goody MOVE.L (SP)+,A1 ; restore DCE pointer
BRA CtlGood ; => return no error
BadCtl MOVE.L (SP)+,A1 ; restore DCE pointer
BRA CtlBad ; => return an error
;
; set up a linear gamma table. To prevent memory thrash, build this new one
; the same size as the existing one (one or three channel)
;
LinearTab
MOVE.L GammaPtr(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
ADDA #GFormulaData,A0 ; point to tables
ADDA D0,A0 ; point past monID, if present
@ChanLoop MOVE.W #255,D0 ; loop count within each channel
@entryLoop MOVE.B D0,(A0) ; write value
NOT.B (A0)+ ; invert it to make table ramp properly
DBRA D0,@entryLoop ; for each entry in channel
DBRA D2,@ChanLoop ; and each channel
BRA.S Goody ; all done
GrayPage ; <EHB12/16>
;---------------------------------------------------------------------
;
; Clear the specified page in the current mode to gray
;
; A0 = Ptr to i/o parameter block
; A1 = Ptr to DCE
; A2 = Ptr to cs parameter record
;
;---------------------------------------------------------------------
MOVE.L dCtlStorage(A1),A3 ; get handle to our storage <EHB12/16>
MOVE.L (A3),A3 ; get pointer to our storage <EHB12/16>
MOVE saveMode(A3),D1 ; D1 = mode <EHB12/16>
BSR ChkMode ; convert mode to depth in D1 <EHB12/16>
BNE CtlBad ; => not a valid depth <C804/12Feb87> DAF
MOVE csPage(A2),D0 ; D0 = page <EHB12/16>
BSR ChkPage ; check page <EHB12/16>
BNE CtlBad ; => not a valid page <EHB12/16>
BSR GrayScreen ; paint the screen gray <EHB12/16>
BRA CtlGood ; => return no error <EHB12/16>
SetGray
;---------------------------------------------------------------------
;
; Set luminance mapping on (csMode = 1) or off (csMode = 0)
;
; When luminance mapping is on, RGB values passed to setEntries are mapped
; to grayscale equivalents before they are written to the CLUT.
;
; A0 = Ptr to i/o parameter block
; A1 = Ptr to DCE
; A2 = Ptr to cs parameter record
;
;---------------------------------------------------------------------
MOVE.L dCtlStorage(A1),A3 ; get handle to our storage
MOVE.L (A3),A3 ; get pointer to our storage
MOVE GFlags(A3),D0 ; get current flags word
MOVE.B csMode(A2),D1 ; get boolean
BFINS D1,D0{16:1} ; set grayFlag
MOVE D0,GFlags(A3)
BRA CtlGood ; => return no error
SetInterrupt
;---------------------------------------------------------------------
;
; Enable (csMode = 0) or disable (csMode = 1) VBL interrupts
;
; As a future performance enhancement, interrupts on the card can be
; disabled or enabled from software. For instance, if the cursor is
; not on a screen, and there is nothing in the Slot Interrupt Queue
; for that device, interrupts may be disabled reducing interrupt
; overhead for the system.
;
; The EnableThem routine is updated to NOT allocate memory when called.
; Now, it reuses the sqElement that was created in Open. This allows this
; routine to be called at interrupt time, and fixes the unbalanced memory
; allocation problems from before.
;
; A0 = Ptr to i/o parameter block
; A1 = Ptr to DCE
; A2 = Ptr to cs parameter record
;
;---------------------------------------------------------------------
WITH VDPageInfo,SlotIntQElement
MOVE.L dCtlStorage(A1),A3 ; get handle to our storage
MOVE.L (A3),A3 ; get pointer to our storage
MOVE GFlags(A3),D0 ; get current flags word
MOVE.B csMode(A2),D1 ; get boolean
BFINS D1,D0{17:1} ; set IntFlag
MOVE D0,GFlags(A3) ; put flag word back
BTST #IntDisFlag,D0 ; is the flag on or off?
BEQ.S EnableThem ; if zero, then enable
; this code disables VBL interrupts, then removes the interrupt handler
BSR WaitVSync ; to be safe, wait for the next VBL
MOVE.L dCtlDevBase(A1),A0 ; get the device base <1.2>
ADD.L #DisableVInt,A0 ; point to hardware register <1.2>
CLR.B (A0) ; hit the register
MOVE.L saveSQElPtr(A3),A0 ; get the SQ element pointer <1.2>
MOVEQ #0,D0 ; clear D0 out <1.2>
MOVE.B dCtlSlot(A1),D0 ; set up slot number <1.2>
_SIntRemove ; remove the interrupt handler
BRA CtlGood ; done
EnableThem
MOVE.L saveSQElPtr(A3),A0 ; get pointer to slot queue element <1.3>
LEA BeginIH,A4 ; save Pointer to interrupt handler
MOVE.W #SIQType,SQType(A0) ; setup queue ID
MOVE.L A4,SQAddr(A0) ; setup int routine address
MOVE.L dctlDevBase(A1),SQParm(A0) ; save slot base addr as A3 parm
CLR.L D0
MOVE.B dctlSlot(A1),D0 ; setup slot #
_SIntInstall ; and do install
BNE CtlBad
MOVE.L dctlDevBase(A1),A0 ; get hardware base
ADD.L #ClrVInt,A0 ; hit the clear register (which also enables)
CLR.B (A0) ; start interrupts happening
BRA CtlGood ; and go home
ENDWITH
;
**********************************************************************
*
* Video Driver Status Call Handler. Right now there are eight calls:
*
* (0) Error
* (1) Error
* (2) GetMode
* (3) GetEntries
* (4) GetPage
* (5) GetPageBase
* (6) GetGray
* (7) GetInterrupt <S306/23Nov87> DAF
* (8) GetGamma <S306/23Nov87> DAF
*
* Entry: A0 = param block
* A1 = DCE pointer
* Uses: A2 = cs parameters (ie. A2 <- csParam(A0)) (must be preserved)
* A3 = scratch (doesnt need to be preserved)
* D0-D3 = scratch (dont need to be preserved)
*
* Exit: D0 = error code
*
**********************************************************************
VideoStatus
MOVE.L A0,-(SP) ; save a register <S406/26Feb88> DAF
MOVE.W csCode(A0),D0 ; get the opCode
MOVE.L csParam(A0),A2 ; A2 <- Ptr to control parameters <22Sep86>
CMP.W #8,D0 ;IF csCode NOT IN [0..8] THEN
BHI.S StatBad ; Error, csCode out of bounds.
LSL.W #1,D0 ;Adjust csCode to be an index into the table.
MOVE.W StatJumpTbl(PC,D0.W),D0 ;Get the relative offset to the routine.
JMP StatJumpTbl(PC,D0.W) ;GOTO the proper routine.
StatJumpTbl DC.W StatBad-StatJumpTbl ;$00 => Error
DC.W StatBad-StatJumpTbl ;$01 => Error
DC.W GetMode-StatJumpTbl ;$02 => GetMode
DC.W GetEntries-StatJumpTbl ;$03 => GetEntries
DC.W GetPage-StatJumpTbl ;$04 => GetPage
DC.W GetPageBase-StatJumpTbl ;$05 => GetPageBase
DC.W GetGray-StatJumpTbl ;$06 => GetGray
DC.W GetInterrupt-StatJumpTbl ;$07 => GetInterrupt
DC.W GetGamma-StatJumpTbl ;$08 => GetGamma
StatBad MOVEQ #statusErr,D0 ; else say we dont do this one
BRA.S StatDone ; and return
StatGood MOVEQ #noErr,D0 ; return no error
StatDone MOVE.L (SP)+,A0 ; restore saved register <S406/26Feb88> DAF
BRA ExitDrvr
GetMode
;---------------------------------------------------------------------
;
; Return the current mode
;
;---------------------------------------------------------------------
MOVE.L dCtlStorage(A1),A3 ; get handle to our storage
MOVE.L (A3),A3 ; get pointer to our storage
MOVE.W saveMode(A3),csMode(A2) ; return the mode
MOVE.W savePage(A3),csPage(A2) ; return the page number
MOVE.L saveBaseAddr(A3),csBaseAddr(A2) ; and the base address
BRA.S StatGood ; => return no error
GetEntries
;---------------------------------------------------------------------
;
; Get the CLUT.
;
;---------------------------------------------------------------------
; Initialize loop.
MOVE.L csTable(A2),D0 ; Check for a nil pointer.
BEQ.S StatBad
MOVE.L D0,A0 ; A0 <- pointer to table.
MOVE.W csCount(A2),D0 ; get the count <1.6/DAF>
CMP.W #-1,csStart(A2) ; is it index or sequence mode? <C5??/04Jan87> DAF
BEQ.S GECom ; if index, then continue <C5??/04Jan87> DAF
MOVE.W D0,D1 ; make a copy for index loop <S190/01Jul87> DAF
MOVE.W D0,D2 ; make a copy of the count <C5??/04Jan87> DAF
ADD.W csStart(A2),D2 ; get last index <C5??/04Jan87> DAF
@1
MOVE.W D2,value(A0,D1*8) ; write the index into the table <C5??/04Jan87> DAF
SUBQ #1,D2 ; decrease index
DBRA D1,@1 ; for all indices <C5??/04Jan87> DAF
GECom
MOVEM.L D6-D7,-(SP) ; SAVE WORK REGISTERS
; Get the index range (D6) and the shift constant (D7).
BSR CvtIndex
; Copy the CLUT to the Table
MOVE.L dCtlDevBase(A1),A3 ; A3 <- base address of device.
ADD.L #ClrTbl,A3 ; Add offset to color regs.
; REPEAT
@Repeat MOVE.W value(A0),D1 ; D1 <- xindex.
CMP.W D6,D1 ; IF D1 > D6 THEN
BHI.S @Until ; GOTO @Until {Index is out of range for this mode}
OR.L #$FFFF0000,D1 ; get ones in hi half of d1.<C389/10Nov86>
ROL.L D7,D1 ; shift it. <C389/10Nov86>
MOVE.B D1,wCLUTAddReg(A3) ; Point to proper entry in CLUT
MOVE.B rCLUTDataReg(A3),D1 ; Get red
NOT.W D1
MOVE.B D1,rgb+red(A0)
MOVE.B D1,rgb+red+1(A0) ; <C715>
MOVE.B rCLUTDataReg(A3),D1 ; Get green
NOT.W D1
MOVE.B D1,rgb+green(A0)
MOVE.B D1,rgb+green+1(A0) ; <C715>
MOVE.B rCLUTDataReg(A3),D1 ; Get blue
NOT.W D1
MOVE.B D1,rgb+blue(A0)
MOVE.B D1,rgb+blue+1(A0) ; <C715>
@Until ADDQ.L #colorSpecSize,A0 ; A0 <- next entry in the table.
DBRA D0,@Repeat ; UNTIL (entire table has been copied)
MOVEM.L (SP)+,D6-D7 ; RESTORE WORK REGISTERS
BRA StatGood ; => return no error
GetPage
;---------------------------------------------------------------------
;
; Return the number of pages in the specified mode
;
;---------------------------------------------------------------------
MOVE csMode(A2),D1 ; get the mode <EHB12/16>
BSR ChkMode ; check mode, get depth in D1 <EHB12/16>
BNE StatBad ; => not a valid mode <EHB12/16>
MOVE.L dCtlStorage(A1),A0 ; get private storage handle <C804/12Feb87> DAF
MOVE.L (A0),A0 ; get pointer <1.3/DAF>
TST.B VRAM256K(A0) ; 512K or 256K? <C804/12Feb87> DAF
BNE.S @1 ; if TRUE, then 256K of RAM <C804/12Feb87> DAF
MOVEQ #4,D0 ; if FALSE, 512K vRAM (and 1 more page per mode) <C804/12Feb87> DAF
BRA.S @2
@1 MOVEQ #3,D0 ; get one-bit page count <EHB12/16>
@2 DIVU D1,D0 ; divide by depth <EHB12/17>
ADD #1,D0 ; make it one based <EHB12/17>
MOVE D0,csPage(A2) ; return page count <EHB12/16>
BRA StatGood ; => return no error <EHB12/16>
GetPageBase
;---------------------------------------------------------------------
;
; Return the base address for the specified page in the current mode
;
;---------------------------------------------------------------------
MOVE.L dCtlStorage(A1),A3 ; get handle to our storage <EHB12/16>
MOVE.L (A3),A3 ; get pointer to our storage <EHB12/16>
MOVE saveMode(A3),D1 ; get the current mode <EHB12/16>
BSR ChkMode ; convert to depth in D1 <EHB12/16>
MOVE.W csPage(A2),D0 ; get the requested page <EHB12/16>
BSR ChkPage ; is the page valid? <EHB12/16>
BNE StatBad ; => no, just return <EHB12/16>
MOVE saveMode(A3),D1 ; get the current mode <EHB12/16>
SUB #OneBitMode,D1 ; make it 0 based <EHB12/17>
LEA ModeTbl,A0 ; point to tables <EHB12/16>
MULU 4(A0,D1*8),D0 ; calc page * rowBytes <EHB12/16>
MULU 6(A0,D1*8),D0 ; calc page * rowBytes * height <EHB12/16>
MOVEQ #defmBaseOffset,D1 ; add offset for TFB <EHB12/16>
ADD.L D1,D0 ; which doesnt use first long <EHB12/16>
ADD.L dCtlDevBase(A1),D0 ; add base address for card <EHB12/17>
MOVE.L D0,csBaseAddr(A2) ; return the base address <EHB12/16>
BRA StatGood ; => return no error
GetGray
;---------------------------------------------------------------------
;
; Return a boolean, set true if luminance mapping is on
;
;---------------------------------------------------------------------
CLR csMode(A2) ; assume that grayFlag is false
MOVE.L dCtlStorage(A1),A3 ; get handle to our storage
MOVE.L (A3),A3 ; get pointer to our storage
TST GFlags(A3) ; is luminance mapping on?
BPL StatGood ; => no, return false
MOVE.B #1,csMode(A2) ; else return true
BRA StatGood ; => and return
GetInterrupt
;---------------------------------------------------------------------
;
; Return a boolean in csMode, set true if VBL interrupts are disabled
;
;---------------------------------------------------------------------
CLR csMode(A2) ; assume that grayFlag is false
MOVE.L dCtlStorage(A1),A3 ; get handle to our storage
MOVE.L (A3),A3 ; get pointer to our storage
BTST #IntDisFlag,GFlags(A3) ; is luminance mapping on?
BEQ StatGood ; => no, return false
MOVE.B #1,csMode(A2) ; else return true
BRA StatGood ; => and return
GetGamma
;---------------------------------------------------------------------
;
; Return the handle to the current gamma table
;
;---------------------------------------------------------------------
MOVE.L dCtlStorage(A1),A3 ; get handle to our storage
MOVE.L (A3),A3 ; get pointer to our storage
MOVE.L GammaPtr(A3),csGTable(A2) ; return the pointer to the structure <S441>
BRA StatGood ; and return a good result
;=====================================================================
;
; Special.
;
;=====================================================================
;---------------------------------------------------------------------
;
; Wait for vertical blanking
;
; A1 = DCE POINTER
;---------------------------------------------------------------------
WaitVSync
Move.l A0,-(Sp) ; Two Moves are faster than a Movem for
Move.l D0,-(Sp) ; two registers.
MOVE.W SR,-(SP) ; preserve the status register <2>
MOVEQ #7,D0 ; get mask in D0 <2>
AND.B (SP),D0 ; get the interrupt level <2>
SUBQ.B #2,D0 ; <2>
BGE.S @OK ; if ≥, then don't change <2>
ORI.W #$0200,SR ; raise above level-2 <2>
ANDI.W #$FAFF,SR ; make it level-2 <2>
@OK
MOVE.L dctlDevBase(A1),A0 ;A3 <- slot base addr <C497>
ADD.L #RdVSync,A0 ;Adjust base addr <C497>
@0 MOVE.B (A0),D0 ;Read the Vert-Sync state, <C497><jmp>: .L to .B
BTST #0,D0 ; and test it. <C497>
BNE.S @0 ;Repeat until it goes low. <C497>
; <C497>
@1 MOVE.B (A0),D0 ;Read the Vert-Sync state, <C497><jmp>: .L to .B
BTST #0,D0 ; and test it. <C497>
BEQ.S @1 ;Repeat until it goes high. <C497>
MOVE (SP)+,SR ;RE-ENABLE CURSOR INTERRUPTS
Move.l (Sp)+,D0 ; Restore work registers.
Move.l (Sp)+,A0
RTS
;---------------------------------------------------------------------
;
; Exit from control or Status.
;
;---------------------------------------------------------------------
ExitDrvr BTST #NoQueueBit,ioTrap(A0) ; no queue bit set?
BEQ.S GoIODone ; => no, not immediate
RTS ; otherwise, it was an immediate call
GoIODone MOVE.L JIODone,A0 ; get the IODone address
JMP (A0) ; invoke it
;=====================================================================
;
; Utilities
;
;=====================================================================
;---------------------------------------------------------------------
;
; CvtIndex
;
; Calculate the proper index for the hw dependent CLUT given a
; hw independent index. (for example: <0,1,2,3> => <$00,$40,$80,$C0>
;
; -> A1 : Ptr to the DCE.
; <- D6 : The Index Range (1,3,15 or 255).
; <- D7 : The Shift Constant (7,6,4 or 0).
;
;---------------------------------------------------------------------
; Save registers
CvtIndex MOVEM.L A3/D0/D4,-(SP) ; Save registers
; Get the mode (D4). mode will be 1,2,4 or 8.
MOVE.L dCtlStorage(A1),A3 ; D7 <- the mode (onebitmode, twobitmode,....)
MOVE.L (A3),A3
MOVE.W saveMode(A3),D7
SUB.W #OneBitMode,D7 ; D7 <- (0,1,2 or 3)
MOVEQ #1,D4 ; D4 <- 1 << D3 {ie. 1,2,4 or 8}
LSL.W D7,D4
; Determine the index range (D6). Range will be in [0..n], we need to calculate n.
MOVE.W D4,D0 ; D0 <- (0,1,3 or 7)
SUBQ #1,D0
MOVEQ #2,D6 ; D6 <- (2 << D0) - 1 {ie. 1,3,15,255}
LSL.W D0,D6
SUBQ #1,D6
; Determine the shift constant (D7). SC = (7,6,4 or 0).
MOVEQ #8,D7 ; D7 <- (7,6,4 or 0)
SUB.W D4,D7
; End
MOVEM.L (SP)+,A3/D0/D4 ; Restore registers
RTS
;---------------------------------------------------------------------
;
; ChkMode
;
; Maps the mode to a depth (1, 2, 4 or 8)
; Assumes DCE pointer in A1.
;
; -> D1: Mode
; <- D1: Depth
; D0: trashed
;
; Returns EQ if mode is valid.
ChkMode SUB #OneBitMode,D1 ; make it 0 based
BMI.S ModeBad ; =>bad mode, return
CMP #3,D1 ; is it too big?
BGT.S ModeBad ; =>yes, return
MOVEQ #1,D0 ; get a one
LSL D1,D0 ; convert to a depth
MOVE D0,D1 ; and return in D1
CMP.W #8,D1 ; requesting 8-Bit mode? <C804/12Feb87> DAF
BNE.S @1 ; 1,2,4 always OK <C804/12Feb87> DAF
MOVE.L A0,-(SP) ; save A0 <C804/12Feb87> DAF
MOVE.L dCtlStorage(A1),A0 ; get handle to private storage <C804/12Feb87> DAF
MOVE.L (A0),A0 ; get pointer to private storage <C804/12Feb87> DAF
TST.B VRAM256K(A0) ; is there too little RAM? <C804/12Feb87> DAF
MOVE.L (SP)+,A0 ; restore register <C804/12Feb87> DAF
BNE.S ModeBad ; if TRUE, then return error <C804/12Feb87> DAF
@1
ModeOK CMP.W D1,D1 ; get EQ
ModeBad RTS ; EQ if valid depth
;---------------------------------------------------------------------
;
; ChkPage
;
; Checks to see if the page in D0 is valid for the depth in D1.
; Max page numbers are 4 for 1 bit mode; 2 for 2 bit mode; 1 for 4 bit mode;
; and 0 for 8 bit mode.
; Assumes DCE pointer in A1.
;
; -> D0: Page
; -> D1: Depth
;
; Returns EQ if page is valid.
ChkPage MOVEM.L D2/A0,-(SP) ; save work registers <EHB12/17><C804/12Feb87> DAF
MOVE.L dCtlStorage(A1),A0 ; get private storage handle <C804/12Feb87> DAF
MOVE.L (A0),A0 ; get pointer to private storage <C804/12Feb87> DAF
TST.B VRAM256K(A0) ; 512K or 256K? <C804/12Feb87> DAF
BNE.S @1 ; if TRUE, then 256K of RAM <C804/12Feb87> DAF
MOVEQ #4,D2 ; if FALSE, 512K vRAM (and 1 more page per mode) <C804/12Feb87> DAF
BRA.S @2
@1 MOVEQ #3,D2 ; get max one bit page count <EHB12/17>
@2 DIVU D1,D2 ; divide by depth <EHB12/17>
CMP D2,D0 ; is page # too big? <EHB12/17>
SGT D2 ; set flag if too big <EHB12/16>
TST.B D2 ; and test condition <EHB12/16>
MOVEM.L (SP)+,D2/A0 ; restore work registers <EHB12/16>
RTS
;---------------------------------------------------------------------
; InitTFB initializes the TFB
; All Registers preserved
;---------------------------------------------------------------------
TFBInit MOVEM.L D0/A0/A1,-(SP) ;save all regs
MOVE.L dctlDevBase(A1),A0 ;A0 <- slot base addr
ADD.L #TFBIBase,A0 ;point to registers
LEA TFBInitTbl,A1 ;get data pointer
MOVEQ #$F,D0 ;do 16 bytes
@0 MOVE.B (A1)+,(A0) ;set one byte
SUBQ #4,A0 ;back up to prev register
DBRA D0,@0 ;repeat until done
MOVEM.L (SP)+,D0/A0/A1 ;restore all regs
RTS ;init done
TFBInitTbl DC.B $DF,$B8,$FF,$FF,$E1,$1A,$88,$B9
DC.B $FA,$FD,$FD,$FE,$F0,$BE,$FA,$37
TFBSetDepth
;---------------------------------------------------------------------
; SetDepth sets the TFB depth
; D1 contains the depth (1,2,4,8}
; A1 = DCE pointer
; A2 = parameter block pointer
; A3 = dCtlStorage pointer
; Preserves all registers
;---------------------------------------------------------------------
; since we cant read the mode from the control registers, save copies
MOVEM.L D0-D2/A0-A1,-(SP) ;save all regs <EHB12/16>
; save the new mode
MOVE.W csMode(A2),saveMode(A3) ; save the mode <EHB12/16>
; Wait for vertical sync. <C497>
BSR WaitVSync ; wait for vertical blanking
MOVE.L dctlDevBase(A1),A0 ;A0 <- slot base addr
ADD.L #TFBBase,A0 ;point to registers
CMP #8,D1 ;depth too big? <22Sep86>
BGT.S NoDepth ;=>yes, just exit <EHB 12/16>
MOVEQ #0,D0 ;index = 0
@5 LSR #1,D1 ;shift depth
BCS.S @10 ;=>got index
ADDQ #1,D0 ;else next index
BRA.S @5 ;=>repeat until done
@10 MOVE.B #TFBReset,TFBResReg(A0) ;put TFB into reset state
LEA DepthTbl,A1 ;point to depth data
LSL #4,D0 ;get index into table <16Oct86 GWN>
ADD D0,A1 ;bump to row for depth
MOVEQ #$F,D0 ;do 16 bytes
@20 MOVE.B (A1)+,D1 ;get a byte
NOT.B D1 ;invert it
MOVE.B D1,(A0) ;write one byte
ADDQ #4,A0 ;bump to next register
DBRA D0,@20 ;=>repeat until done
NoDepth MOVEM.L (SP)+,D0-D2/A0-A1 ;restore all regs <EHB 12/16>
RTS ;return
DepthTbl DC.B $20,$47,$00,defmBaseOffset/4,$1E,$E5,$77,$46,$05,$02,$02,$01,$0F,$41,$05,$C8 ;<C439/18Nov86>
DC.B $40,$47,$00,defmBaseOffset/4,$3C,$E5,$77,$46,$05,$06,$06,$04,$20,$04,$0B,$D8 ;<C439/18Nov86>
DC.B $80,$47,$00,defmBaseOffset/4,$78,$E5,$77,$46,$05,$0E,$0E,$0A,$42,$8A,$16,$E8 ;<C439/18Nov86>
DC.B $00,$47,$00,defmBaseOffset/4,$F0,$E5,$77,$46,$05,$1E,$1E,$16,$86,$96,$2D,$F9 ;<C439/18Nov86>
TFBSetPage
;---------------------------------------------------------------------
; Entry: D0 = Page # (0 based)
; A1 = DCE pointer
; A2 = parameter block pointer
; A3 = dCtlStorage pointer
;
; The base of a page is at dCtlDevBase + 4 + (page * RowBytes * height)
; The 4 is because we dont use the first long in the TFB to avoid
; negative addressing problems.
;
; All registers are preserved.
MOVEM.L D0-D1/A0-A1,-(SP) ; save all registers <EHB12/16>
MOVE D0,savePage(A3) ; save the page <EHB12/16>
MOVE csMode(A2),D1 ; get the mode <EHB12/17>
SUB #OneBitMode,D1 ; make it 0 based <EHB12/17>
LEA ModeTbl,A0 ; point to tables <EHB12/16>
MULU 4(A0,D1*8),D0 ; calc page * rowBytes <EHB12/16>
MULU 6(A0,D1*8),D0 ; calc page * rowBytes * height <EHB12/16>
MOVEQ #defmBaseOffset,D1 ; add offset for TFB <EHB12/16>
ADD.L D1,D0 ; which doesnt use first long <EHB12/16>
MOVE.L D0,D1 ; save offset to page in bytes <EHB12/16>
MOVE.L dCtlDevBase(A1),A0 ; get base for device <EHB12/16>
ADD.L A0,D0 ; add the slots base address <EHB12/16>
MOVE.L D0,saveBaseAddr(A3) ; save the base address <EHB12/16>
; Now set the hardware by writing the offset in longs to the proper registers.
LSR.L #2,D1 ; convert offset to longs <EHB12/16>
ADD.L #TFBBase,A0 ; point to registers <EHB12/16>
NOT D1 ; invert the value <EHB12/16>
MOVE.B D1,wBaseOfstLo(A0) ; set low byte of offset <EHB12/16>
LSR #8,D1 ; get high byte of offset <EHB12/16>
MOVE.B D1,wBaseOfstHi(A0) ; set high byte of offset <EHB12/16>
MOVEM.L (SP)+,D0-D1/A0-A1 ; restore all registers <EHB12/16>
RTS ; and return <EHB12/16>
GrayScreen
;---------------------------------------------------------------------
; D0 = Page
; D1 = Mode spID <1.6/DAF>
; A3 = dCtlStorage Ptr.
;
; All registers are preserved.
MOVEM.L D0-D4/A0-A1,-(SP) ; save all registers <EHB12/16>
;+++ <1.6/DAF> MOVE csMode(A2),D1 ; get the mode <EHB12/17>
MOVE saveMode(A3),D1 ; get mode ID from driver privates rather <1.7>
; than csBlock, so you dont need to pass
; this to Control_GrayPage
SUB #OneBitMode,D1 ; make it 0 based <EHB12/17>
CMP.W #3,D1 ; if 8-bit req, test for RAM <C804/13Feb87> DAF
BNE.S @1 ; 1,2,4 bit requests are always OK <C804/13Feb87> DAF
TST.B VRAM256K(A3) ; is there enough RAM? <C804/13Feb87> DAF
BNE.S GSDone ; if TRUE, then dont do anything <C804/13Feb87> DAF
@1 LEA ModeTbl,A1 ; point to tables <EHB12/16>
MOVE.L 0(A1,D1*8),D0 ; D0 = the proper pattern <EHB12/16>
MOVE 4(A1,D1*8),D4 ; D4 = rowbytes for the screen <EHB12/16>
MOVE 6(A1,D1*8),D3 ; D3 = screen height <EHB12/16>
SUBQ #1,D3 ; make it 0 based <EHB12/16>
MOVE.L D0,D1 ; get inverse of pattern <EHB12/16>
NOT.L D1 ; for alternate lines <EHB12/16>
MOVE.L saveBaseAddr(A3),A1 ; point to the current page <EHB12/16>
NxtRow1 MOVE.L A1,A0 ; get next row <EHB12/16>
MOVE.W D4,D2 ; get bytes per row <EHB12/16>
LSR #2,D2 ; get longs per row <EHB12/16>
SUBQ #1,D2 ; make count 0 based <EHB12/16>
NxtLong1 MOVE.L D0,(A0)+ ; write gray <EHB12/16>
DBF D2,NxtLong1 ; for entire width of row <EHB12/16>
EXG D0,D1 ; get inverse gray for next row <EHB12/16>
ADD.W D4,A1 ; bump to next row <EHB12/16>
DBF D3,NxtRow1 ; until no more rows <EHB12/16>
GSDone
MOVEM.L (SP)+,D0-D4/A0-A1 ; restore all registers <EHB12/16>
RTS ; and return <EHB12/16>
; Mode info: Pattern,RowBytes,Height
ModeTbl DC.W $AAAA,$AAAA,$0080,$01E0 ; one bit per pixel <EHB12/16>
DC.W $CCCC,$CCCC,$0100,$01E0 ; two bit per pixel <EHB12/16>
DC.W $F0F0,$F0F0,$0200,$01E0 ; four bit per pixel <EHB12/16>
DC.W $FF00,$FF00,$0400,$01E0 ; eight bit per pixel <EHB12/16>
;------------------------------------------------------------- <19Sep86>
; The Interrupt handler for TFB board
;-------------------------------------------------------------
; The interrupt Handler
; On entry A1 contains the slot base address
; D0-D3/A0-A3 have been preserved.
BeginIH MOVE.L A1,A0 ; get screen base
MOVE.L A1,D0 ; and save for later <C371>
ADD.L #ClrVInt,A0 ; get offset to register
CLR.B (A0) ; clear interrupt from card
; D0 = $Fssxxxxx
ROL.L #8,D0 ; D0 <- $sxxxxxFs Convert the address into
AND #$0F,D0 ; D0 <- $sxxx000s the slot number.
MOVE.L JVBLTask,A0 ; call the VBL task manager <C371>
JSR (A0) ; with slot # in D0 <C371>
MOVEQ #1,D0 ; signal that int was serviced
RTS ; and return to caller
;-------------------------------------------------------------
; (TERROR) ROM Support (for loading at GetDriver time)
;-------------------------------------------------------------
If ForRom Then
Export TFBDrvrSize
TFBDrvrEnd
TFBDrvrSize Dc.l TFBDrvrEnd-TFBDrvr
EndP
Endif
End