mac-rom/DeclData/DeclVideo/V8/V8Driver.a

1830 lines
60 KiB
Plaintext

;
; File: V8Driver.a
;
; Contains: This file contains the video driver for use by the Macintosh
; OS for the Elsie/V8 hardware.
;
; Written by: David Fung/Mike Puckett
;
; Copyright: © 1990-1993 by Apple Computer, Inc. All rights reserved.
;
; Change History (most recent first):
;
; <SM5> 01-08-93 jmp Updated the version number from 0.0 (LC/LC II) to 0.1 (LC930).
; <SM4> 01-07-93 jmp Fixed a problem where plugging in a VGA-sensed would cause the
; death-chimes to play (had to do with the fact that we donÕt
; support gamma tables on VGA displays).
; <SM3> 11/5/92 SWC Changed VideoEqu.a->Video.a and ShutdownEqu.a->Shutdown.a.
; <SM2> 11/2/92 kc Don't include SonicEqu.a.
; <1> 10/6/92 GDW New location for ROMLink tool.
; <SM4> 09-03-92 jmp (jmp,H6) A couple of labels in the GetEntries code we exiting
; thru StatGood/StatBad instead of their respective V8
; counterparts.
; (jmp,H5) Corrected .s vs. non-.s branches and odd-alignment
; problems.
; <SM3> 6/4/92 KW (jmp,H4) Eliminated support for the no-vRAM case.
; (jmp,H3) Changed the way the base address is gotten and saved.
; (Use the dCtlDevBase instead of VideoInfo due to the differences
; between the video base with and without vRAM installed.)
; (jmp,H1) Roll-in LC II changes. In routine "V8SetEntries" we
; should have been exiting through V8SEDone instead of SEDone.
; ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
; Pre-SM ROM comments begin here.
; ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
; <2> 3/31/92 JSM Rolled this file into Reality.
; <1> 12/8/90 HJR First time for Terror.
; <8> 9/17/90 DAF Integrated code review changes. Relocated fake prime installing
; routine to Open call, fixed 16-bit gray pattern for alpha,
; corrected error code ordering in Open, optimized graying loops
; in two places, improved status flag setting in V8DisableGuts
; <7> 9/5/90 JJ Rex V8: Fix bug in VDAC equates used in GetEntries.
; <6> 9/5/90 JJ Fixed screen graying bug in non-vRAM HiRes mode
; <5> 9/5/90 JJ Corrected bug in screen graying utility (incorrect offset
; between 256K and 512K tables).
; <4> 8/30/90 DAF Fix Apple // mode setting in V8SetDepth
; <3> 8/16/90 JJ Added GetEntries, updated various routines for all vRAM
; configurations, updated equate names / DAF
; <2> 8/8/90 JJ Fixed CLUT graying in direct (16-bit) mode
; <1> 8/7/90 JJ Added Elsie/V8 video driver
STRING C
PRINT OFF
LOAD 'StandardEqu.d'
INCLUDE 'DockingEqu.a'
INCLUDE 'EgretEqu.a'
INCLUDE 'GestaltEqu.a'
INCLUDE 'GestaltPrivateEqu.a'
INCLUDE 'HardwarePrivateEqu.a'
INCLUDE 'IOPrimitiveEqu.a'
INCLUDE 'PowerPrivEqu.a'
INCLUDE 'ROMEqu.a'
INCLUDE 'Video.a'
INCLUDE 'SlotMgrEqu.a'
INCLUDE 'ShutDown.a'
; INCLUDE 'SonicEqu.a'
INCLUDE 'UniversalEqu.a'
INCLUDE 'DepVideoEqu.a'
PRINT ON
SEG '_sV8Driver'
BLANKS ON
STRING ASIS
MACHINE MC68020
; This is device storage which is stored in the dCtlStorage field of the DCE.
V8VidPrivates RECORD 0
saveBaseAddr DS.L 1 ; the screen base address
saveSQElPtr DS.L 1 ; the SQ element pointer (for _SIntRemove).
saveGammaPtr DS.L 1 ; the pointer to the Gamma correction table
saveGamDispPtr DS.L 1 ; the pointer to the Gamma block
saveVDACBase DS.L 1 ; the base addr of the VDAC
GFlags DS.W 1 ; flags word
saveMode DS.W 1 ; the current mode setting
saveID DS.W 1 ; monitor type ID
saveSlotId DS.W 1 ; spID of video sRsrc (hi-order byte only!)
V8VidPrivSize EQU *
ENDR
LV8Driver MAIN EXPORT
;-------------------------------------------------------------------
; Video Driver Header
;-------------------------------------------------------------------
V8Drvr DC.W $4C00 ; ctl,status,needsLock
DC.W 0,0,0 ; not an ornament
; Entry point offset table
DC.W V8VidOpen-V8Drvr ; open routine
DC.W V8Drvr-V8Drvr ; no prime in normal video drivers
DC.W V8VidCtl-V8Drvr ; control
DC.W V8VidStatus-V8Drvr ; status
DC.W V8VidClose-V8Drvr ; close
STRING Pascal
V8VidTitle DC.B '.Display_Video_Apple_V8'
ALIGN 2 ; make sure we're aligned
DC.W CurV8DrvrVersion ; current version
STRING ASIS
;
; V8CLUTTbl contains information required to write to the CLUT in the different screen depths.
; Each depth's information has three values. The first is the number of entries-1 in this depth
; for range checking. The second is the address of the first active CLUT position for that
; screen depth. The last number is the ÒskipÓ factor. Unlike the Bt478 vDAC in the ci and Erickson,
; the Ariel only has adjacent active positions in eight-bit mode. In the lesser depths, the active
; entries are distributed throughout the CLUT address space. As a result, we use sequential CLUT mode
; ONLY in eight-bit mode! The skip factor is the address difference between adjacent active positions
; in each mode.
;
; Generally, these rules are true for any particular depth:
; #entries = (2^^depth)-1
; startposition = (256 / (2^^depth))-1
; skipfactor = 256 / (2^^depth)
V8CLUTTbl
DC.B $01,$7F,$00,$80 ; for one-bit mode
DC.B $03,$3F,$00,$40 ; for two-bit mode
DC.B $0F,$0F,$00,$10 ; for four-bit mode
DC.B $FF,$00,$00,$01 ; for eight-bit mode
CLUTDatRec RECORD 0 ;
Range DS.B 1 ; maximum index value in this depth
Start DS.B 1 ; lowest active CLUT address
Skip DS.W 1 ; skip value between active addresses
; (it's a .W to simplify multiplies!)
ENDR
**********************************************************************
*
* V8VidOpen allocates private storage for the device in the DCE and locks
* it down for perpetuity. Also, install the interrupt handler and enable
* the interrupts.
*
* Entry: A0 = param block pointer
* A1 = DCE pointer
*
* Locals: A3 = pointer to private storage
*
**********************************************************************
WITH VDPageInfo,SlotIntQElement,V8VidPrivates
V8VidOpen
;
; Allocate private storage (since block is CLEAR, GFlags are zeroed) and get
; a pointer to it in A3
;
MOVEQ #V8VidPrivSize,D0 ; get size of parameters
_ResrvMem ,SYS ; make room as low as possible
MOVEQ #V8VidPrivSize,D0 ; get size of parameters
_NewHandle ,SYS,CLEAR ; get some memory for private storage
BNE @OpErrorC ; => return an error in open
MOVE.L A0,dCtlStorage(A1) ; save returned handle in DCE
_HLock ; and lock it down forever (this includes a Time Mgr QElem)
MOVE.L (A0),A3 ; get a pointer to it
;
; remember the VDAC base address since it's hard to look up.
;
WITH ProductInfo,DecoderInfo,VideoInfo
MOVE.L UnivInfoPtr,A0 ; get a pointer to universal data
ADD.L DecoderInfoPtr(A0),A0 ; point to the base address table
MOVE.L VDACAddr(A0),saveVDACBase(A3) ; save pointer
ENDWITH
;
; remember the frame buffer base as well. The Open call doesn't set the video mode
; or page, but SetMode will based on this value
;
MOVE.L dCtlDevBase(A1),saveBaseAddr(A3) ; save the screen base address
MOVE.B dCtlSlotId(A1),saveSlotId(A3) ; save video sRsrc ID
;
; Get and install the interrupt handler. Call the EnableVGuts utility code to do
; this. This utility also starts the interrupts going. If there is an error
; condition, EnableVGuts returns with Z-bit cleared.
MOVEQ #sqHDSize,D0 ; allocate a slot queue element
_NewPtr ,SYS,CLEAR ; get it from system heap cleared
BNE @OpErrorB ; if not allocated, return bad
MOVE.L A0,saveSQElPtr(A3) ; save the SQ element pointer.
BSR V8EnableVGuts ; do it
BNE @OpErrorB ;
;
; read the V8 to find out what kind of monitor we have. To have gotten here, we MUST
; have a valid video device.
;
MOVE.L V8,A2 ; get the V8 base address
MOVE.B V8MonP(A2),D0 ; get the monitor type
LSR.B #3,D0 ; shift Monitor ID into the low bits
AND.W #7,D0 ; lo 3 bits only
MOVE.W D0,saveID(A3) ; remember it for later
;
; load the default gamma table from the slot resource list
;
@Gamma
WITH spBlock
SUBA #spBlockSize,SP ; make a slot parameter block
MOVE.L SP,A0 ; get pointer to block in A0
MOVE.B dCtlSlot(A1),spSlot(A0) ; copy the slot number
MOVE.B dCtlSlotId(A1),spID(A0) ; copy the spID of the video sRsrc <2.0>
CLR.B spExtDev(A0) ; <2.0>
CLR.B spHWDev(A0) ; ????
_sRsrcInfo ; get the spsPointer <2.0>
MOVE.B #sGammaDir,spID(A0) ; look for the gamma directory
_sFindStruct ; get that baby
BNE.S @DoLinear ; if failed, then do linear
MOVE.B #$80,spID(A0) ; get the default gamma table, (always 128)
_sGetBlock ; we can use this since we want it on the sys heap
BNE.S @DoLinear ; if failed, then do linear
;
; skip over header
;
MOVE.L spResult(A0),A0 ; point to head of the block
MOVE.L A0,saveGamDispPtr(A3) ; save the ptr to the gamma block
ADDA #2,A0 ; skip resID
@Name TST.B (A0)+ ; skip over gamma name
BNE.S @Name ;
MOVE.L A0,D0 ; get in d-reg
ADDQ #1,D0 ; word align pointer
BCLR.L #0,D0 ; round it off
MOVE.L D0,saveGammaPtr(A3) ; put it in private storage
;
; Build a linear default gamma table if necessary.
;
@DoLinear
Moveq #gFormulaData,D0 ; Get gamma table header size.
Add #256,D0 ; Add in one-byte per entry.
_NewPtr ,SYS,CLEAR ; Clear it.
Bne @OpErrorA ; If failed, quit.
Move.l A0,saveGamDispPtr(A3) ; Save head of gamma table for disposal.
Move.l A0,saveGammaPtr(A3) ; Head and top are same here.
Move.w #drHwElsie,gType(A0) ; Set up gType.
Move.w #1,gChanCnt(A0) ; Set up gChanCnt.
Move.w #256,gDataCnt(A0) ; Set up gDataCnt.
Move.w #8,gDataWidth(A0) ; Set up gDataWidth.
Adda #gFormulaData+256,A0 ; Point to end of data table.
Move.w #255,D0 ; Set up loop counter.
@Loop Move.b D0,-(A0) ; Write out value.
Dbra D0,@Loop ; Loop.
ADDA #spBlockSize,SP ; release the parameter block
;
; all done!
;
@AllDone MOVEQ #0,D0 ; no error
@EndOpen RTS ; return
@OpErrorA ADDA #spBlockSize,SP ; release the spBlock
@OpErrorB MOVE.L dCtlStorage(A1),A0 ; get the private storage back
_DisposHandle ; release the driver private storage
@OpErrorC MOVE.L #OpenErr,D0 ; say can't open driver
BRA.S @EndOpen
ENDWITH
**********************************************************************
*
* Video Driver Control Call Handler. There are nine calls:
*
* (0) Reset (VAR mode, page: INTEGER; VAR BaseAddr: Ptr);
* (1) KillIO
* (2) SetMode(mode, page: INTEGER; VAR BaseAddr: Ptr);
* (3) SetEntries ( Table: Ptr; Start,Count : integer );
* (4) SetGamma ( Table : Ptr );
* (5) GrayPage (page);
* (6) SetGray (csMode = 0 for color, 1 for gray)
* (7) SetInterrupt ( csMode = 0 for enable, 1 for disable)
* (8) DirectSetEntries (not implemented)
* (9) SetDefaultMode
*
* Entry: A0 = param block pointer
* A1 = DCE pointer
* Uses: A2 = cs parameters (ie. A2 <- csParam(A0)) (must be preserved)
* A3 = scratch (doesn't need to be preserved)
* A4 = scratch (must be preserved)
* D0-D3 = scratch (don't need to be preserved)
*
* Exit: D0 = error code
*
**********************************************************************
;
; Decode the call
;
V8VidCtl MOVE.L A0,-(SP) ; save work registers (A0 is saved because it is used by ExitDrvr)
MOVE.W csCode(A0),D0 ; get the opCode
CMP.W #128,D0 ; is it the restart graying call (id=128?)
BNE.S @0
BRA V8Goodbye ; do the graying call
@0
MOVE.L csParam(A0),A2 ; A2 <- Ptr to control parameters
MOVE.L dCtlStorage(A1),A3
MOVE.L (A3),A3 ; get pointer to private storage
CMP.W #9,D0 ; IF csCode NOT IN [0..9] THEN
BHI.S V8CtlBad ; Error, csCode out of bounds
MOVE.W V8CtlJumpTbl(PC,D0.W*2),D0 ; Get the relative offset to the routine
JMP V8CtlJumpTbl(PC,D0.W) ; GOTO the proper routine
V8CtlJumpTbl
DC.W V8VidReset-V8CtlJumpTbl ; $00 => VidReset
DC.W V8CtlGood-V8CtlJumpTbl ; $01 => CtlGood (no async routines here)
DC.W V8SetVidMode-V8CtlJumpTbl ; $02 => SetVidMode
DC.W V8SetEntries-V8CtlJumpTbl ; $03 => SetEntries
DC.W V8SetGamma-V8CtlJumpTbl ; $04 => SetGamma
DC.W V8GrayPage-V8CtlJumpTbl ; $05 => GrayPage
DC.W V8SetGray-V8CtlJumpTbl ; $06 => SetGray
DC.W V8SetInterrupt-V8CtlJumpTbl ; $07 => SetInterrupt
DC.W V8DirectSetEntries-V8CtlJumpTbl ; $08 => DirectSetEntries
DC.W V8SetDefaultMode-V8CtlJumpTbl ; $09 => SetDefaultMode
V8CtlBad MOVEQ #controlErr,D0 ; else say we don't do this one
BRA.S V8CtlDone ; and return
V8CtlGood MOVEQ #noErr,D0 ; return no error
V8CtlDone MOVE.L (SP)+,A0 ; restore registers.
BRA V8ExitDrvr
V8VidReset
;---------------------------------------------------------------------
;
; Reset the card to its default
;
;---------------------------------------------------------------------
WITH V8VidPrivates
MOVE #FirstVidMode,csMode(A2) ; return default mode
MOVE #FirstVidMode,saveMode(A3) ; remember FirstVidMode as the requested mode
MOVE #1,D1 ; get default depth in D1
@2 MOVEQ #0,D0 ; get page in D0
MOVE D0,csPage(A2) ; return the page
BSR V8SetDepth ; set the depth from D1
MOVE.L saveBaseAddr(A3),csBaseAddr(A2) ; return the base address
BSR V8GrayScreen ; paint the screen gray
BRA.S V8CtlGood ; => no error
ENDWITH
V8SetVidMode
;---------------------------------------------------------------------
;
; Set the card to the specified mode. Only page zero is possible,
; so we need to check that the request was OK.
;
; If the card is already set to the specified mode, then do nothing.
;
;---------------------------------------------------------------------
WITH V8VidPrivates
MOVE.W csMode(A2),D1 ; D1 = mode
BSR V8ChkMode ; check mode and convert
BNE.S V8CtlBad ; => not a valid mode
TST.W csPage(A2) ; only page zero is valid
BNE.S V8CtlBad ; => not a valid page
; Only set if mode has changed
MOVE.W csMode(A2),D2 ; get the mode spID (D1 has the zero-based mode)
CMP saveMode(A3),D2 ; has the mode changed?
BEQ.S @ModeOK1 ; if not, then skip graying get out
; remember the newly requested mode
MOVE.W D2,saveMode(A3) ; remember requested mode
; set the entire color table to gray before switching to avoid screen anomalies
MOVE.L saveGammaPtr(A3),A0 ; get pointer to gamma data structure
MOVE GFormulaSize(A0),D3 ; get the size of formula data
MOVE.W GDataCnt(A0),D2 ; get number of gamma entries
LSR.W #1,D2 ; divide by two to find midpoint
LEA GFormulaData(A0),A0 ; point to formula data
ADD D3,A0 ; first correction table starts here
MOVE.B (A0,D2),D3 ; get corrected gray from middle of red table
; raise to interrupt level 2 the new, good way
MOVE.W SR,-(SP) ; preserve the status register
MOVEQ #7,D2 ; get mask in D0
AND.B (SP),D2 ; get the interrupt level
SUBQ.B #2,D2 ;
BGE.S @OK ; if ³, then don't change
ORI.W #$0200,SR ; raise above level-2
ANDI.W #$FAFF,SR ; make it level-2
@OK
BSR V8WaitVSync ; wait for next blanking period
; we don't need to test the entire mode here, just blank everything
MOVE.L saveVDACBase(A3),A0 ; get the VDAC base addr
ADDA #V8DACwDataReg,A0 ; point to data register
CLR.B V8DACwAddReg-V8DACwDataReg(A0) ; start at the beginning of CLUT, 4-bit mode
MOVE #(256*3)-1,D2 ; get count <8>
@Repeat MOVE.B D3,(A0) ; put component (red=green=blue) <8>
DBRA D2,@Repeat ;
MOVE (SP)+,SR ; restore the status reg
BSR V8SetDepth ; set the depth from D1, page from D0
@ModeOK1
MOVE.L saveBaseAddr(A3),csBaseAddr(A2) ; return the base address
BRA V8CtlGood ; => return no error
ENDWITH
V8SetEntries
;---------------------------------------------------------------------
;
; Input :
; csParam -> datablock
; datablock = csTable -> table of colorSpecs (not colortable)
; csStart -> where to start setting, or -1
; csCount -> # of entries to change
;
; This call has two modes. In SEQUENCE mode, csCount entries are changed
; in the CLUT, starting at csStart. In INDEX mode, csCount entries are
; installed into the CLUT at the positions specified by their .value fields.
; This mode is selected by passing csStart = -1. In both cases, entries are
; range-checked to the dynamic range of the video mode (bits/pixel).
;
;---------------------------------------------------------------------
;
; Set the CLUT
; A0 = Ptr to the table
; A1 = Ptr to DCE
; A2 = Ptr to cs parameter record
; A3 = Ptr to private data, later to CLUT constants table
; A4 = Ptr to gamma red table
; A5 = Ptr to gamma green table
; A6 = Ptr to gamma blue table
;
; D0-D3 = Scratch
; D4 = Size of stack color table buffer
; D5 = GFlags word
; D6 = Index range [0..n]
; D7 = gamma channel size in bits
;
;---------------------------------------------------------------------
; Initialize loop.
WITH V8VidPrivates
BTST #IsDirect,GFlags(A3) ; are we in a direct mode?
BNE V8CtlBad ; error if so
SEGuts
TST.L csTable(A2) ; Check for a nil pointer
BEQ V8CtlBad
MOVEM.L A1/A4-A6/D4-D7,-(SP) ; save registers for gamma
MOVE.W GFlags(A3),D5 ; get GFlags word in D5 (sequence bits are always cleared here)
CMP.W #-1,csStart(A2) ; is it sequence mode?
BEQ.S @5 ; nope, it's index
BSET #PsuedoIndex,D5 ; turn on the bit that denotes a seq write that was xlated to indexed
CMP.W #FourthVidMode,saveMode(A3) ; is it 8- or 16-bit mode?
BLT.S @5 ; if not, need to use indexed mode
BSET #UseSeq,D5 ; if sequence then set bit
@5
MOVE.L saveGammaPtr(A3),A0 ; get pointer to gamma data structure
MOVE.W GFormulaSize(A0),D0 ; get the size of formula data
LEA GFormulaData(A0),A4 ; point to formula data
ADD D0,A4 ; red correction table starts here
MOVE.L A4,A5 ; get default pointer to green data
MOVE.L A4,A6 ; get default pointer to blue data
MOVE GDataWidth(A0),D7 ; get width of each entry in bits
CMP #1,GChanCnt(A0) ; if only only one table, we're set
BEQ.S @OneTbl ; => just one table
MOVE GDataCnt(A0),D0 ; get # entries in table
MOVE D7,D1 ; copy it to goof around
ADD #7,D1 ; round to nearest byte
LSR #3,D1 ; get bytes per entry
MULU D1,D0 ; get size of table in bytes
ADDA D0,A5 ; calc base of green
ASL #1,D0 ; mulu times 2
ADDA D0,A6 ; calc base of blue
@OneTbl
; save the VDAC base address for later. We're going to torch the privates reg (A3) now
MOVE.L saveVDACBase(A3),D6 ; save VDAC base from driver privates
; get the maximum number of entries, zero based from a convenient table
MOVE.W saveMode(A3),D1 ; get the current video mode
SUB.W #FirstVidMode,D1 ; convert to index
WITH CLUTDatRec
LEA (V8CLUTTbl,PC,D1*4),A3 ; point to table of CLUT data
;
; allocate a temporary color table on the stack. We'll pre-process all the entries that will
; change here so we can hit the hardware as quickly as possible.
;
MOVE.W csCount(A2),D3 ; get the number of entries to change
CMP.B Range(A3),D3 ; is it in the allowable range?
BHI V8BadExit ; if outside, then exit w/bad result
MOVE.L D3,D4 ; make a copy of the table size (zero-based!)
ADDQ #1,D4 ; make it a counting number
BTST #UseSeq,D5 ; are we in index or sequential mode?
BEQ.S @isIndex ; if equal, we're indexed
@isSeq MULU #3,D4 ; multiply times 3 for sequential mode
BRA.S @allocIt ; and continue
@isIndex ASL #2,D4 ; multiply times 4 (always less than 1024)
@allocIt SUB.W D4,SP ; allocate the buffer
;
; construct the stack version of the color table. It looks like a color table, but each of the
; four components is only eight bits (rather than 16).
;
MOVE.L SP,A0 ; copy the stack buffer pointer
MOVE.L csTable(A2),A1 ; get colorSpec pointer in A1
; Death! Totally out of registers in this routine, so I'm using the top half of D4 (the temp buffer
; size as the sequence counter used in most video modes to translate sequential requests into
; the indexed write that the hardware needs.
SWAP D4 ; flip the buffer size to the top half
MOVE.W csStart(A2),D4 ; pick up the sequential start position. It might
; be -1 on a true indexed write, but we won't
; use it below if it is.
; write the index if in indexed mode. If in sequential eight mode, blow it off
; completely, since it won't be needed.
@SetupLoop
MOVE.W (A1)+,D0 ; get index
BTST #UseSeq,D5 ; is it sequence mode?
BNE.S @SLSeq ; yup, so go there
BTST #PsuedoIndex,D5 ; was this an indexed request, or is it a non-8bit sequenc
BEQ.S @IndexPresent ;
; this case is a sequential request in a screen depth that does not allow sequential CLUT writes
; (any non-8/16 bit mode). In this case, we substitute the sequence counter for D0 on each
; entry.
MOVE.W D4,D0 ; copy sequence counter to D0
ADDQ.W #1,D4 ; increment sequence counter
@IndexPresent
MULU.W Skip(A3),D0 ; calc the new position at this depth
ADD.B Start(A3),D0 ; add the first entry offset
MOVE.B D0,(A0)+ ; put in stack table
@SLSeq
MOVE.W (A1)+,D0 ; get red
MOVE.W (A1)+,D1 ; get green
MOVE.W (A1)+,D2 ; get blue
TST D5 ; test hi bit of the flags
BPL.S @NoGray ; if not set, don't luminence map
BTST #IsDirect,D5 ; test for direct mode as well
BNE.S @NoGray ; don't allow luminence mapping in direct mode
; we're luminence mapping here
MULU #$4CCC,D0 ; multiply by red weight (0.30)
MULU #$970A,D1 ; multiply by green weight (0.59)
MULU #$1C29,D2 ; multiply by blue weight (0.11)
ADD.L D1,D0 ; sum red and green
ADD.L D2,D0 ; blue also
BFEXTU D0{0:D7},D1 ; get gChanWidth bits for gamma table lookup
MOVE.W D1,D0 ; copy into red register
MOVE.W D1,D2 ; copy into blue register
BRA.S @WriteSP ; go on and write it in the stack buffer
@NoGray
BFEXTU D0{16:D7},D0 ; get gChanWidth bits of red
BFEXTU D1{16:D7},D1 ; get gChanWidth bits of green
BFEXTU D2{16:D7},D2 ; get gChanWidth bits of blue
@WriteSP
MOVE.B (A4,D0),(A0)+ ; write gamma corrected red
MOVE.B (A5,D1),(A0)+ ; write gamma corrected green
MOVE.B (A6,D2),(A0)+ ; write gamma corrected blue
DBRA D3,@SetupLoop ; and loop for each entry
ENDWITH
SWAP D4 ; put the temp buffer size back in the lo-half
;
; OK, the stack table is set up. Now let's load the hardware.
;
MOVE.W csCount(A2),D3 ; get the count again
MOVE.L D6,A3 ; get VDAC base that we pushed above
LEA V8DACwDataReg(A3),A3 ; point to vDAC data write register
MOVE.W SR,-(SP) ; preserve the status register <2>
MOVEQ #7,D0 ; get mask in D0 <2>
AND.B (SP),D0 ; get the interrupt level <2>
SUBQ.B #2,D0 ; <2>
BGE.S @OK ; if ³, then don't change <2>
ORI.W #$0200,SR ; raise above level-2 <2>
ANDI.W #$FAFF,SR ; make it level-2 <2>
@OK
BSR V8WaitVSync ; wait for next blanking period (preserves A0)
LEA 2(SP),A0 ; point to the stack buffer again
BTST #UseSeq,D5 ; is it sequence mode?
BNE.S @SeqWrite ; yup, sequence mode, so go
;
; here's the loop that actually writes to the hardware when in indexed mode
;
@IndexWrite
MOVE.B (A0)+,V8DACwAddReg-V8DACwDataReg(A3) ; write the index value to the CLUT address
MOVE.B (A0)+,(A3) ; write red
MOVE.B (A0)+,(A3) ; write green
MOVE.B (A0)+,(A3) ; write blue
DBRA D3,@IndexWrite ; and loop
BRA.S V8SEDone ; <H1>
; write the translated starting position for sequence mode
@SeqWrite
MOVE.W csStart(A2),D0 ; get sequence start address (exact since it has to be 8-bit mode)
MOVE.B D0,V8DACwAddReg-V8DACwDataReg(A3) ; write the sequence start position
;
; here's the loop that actually writes to the hardware when in sequence mode
;
@SeqLoop
MOVE.B (A0)+,(A3) ; get red
MOVE.B (A0)+,(A3) ; get green
MOVE.B (A0)+,(A3) ; write blue
DBRA D3,@SeqLoop ; and loop
;
; clean up and go home
;
V8SEDone
MOVE (SP)+,SR ; restore status register
ADD D4,SP ; release stack buffer
MOVEM.L (SP)+,A1/A4-A6/D4-D7 ; restore registers
BRA V8CtlGood ; return O-Tay!
V8BadExit
ADDA #4,SP ; kill pushed vDAC address
MOVEM.L (SP)+,A1/A4-A6/D4-D7 ; restore registers
BRA V8CtlBad ; return an error code
ENDWITH
V8SetGamma
;---------------------------------------------------------------------
;
; Set the gamma table. This call copies the supplied gTable so the
; caller does not have to put the source on the system heap. It
; tests if the gamma table is exactly a match to the currently
; connected monitor, or always allows it if the monitor number in
; the FormulaData is -1. If supplied gamma table ptr is NIL, then
; it loads a linear gamma table into the private table
;
; A0 = Ptr to private storage
; A1 = Ptr to DCE
; A2 = Ptr to cs parameter record
;
;---------------------------------------------------------------------
WITH V8VidPrivates
; get new gamma table and check that we know how to handle it
MOVE.L csGTable(A2),D0 ; test for a NIL pointer
BEQ V8LinearTab ; if so, then set up a linear gamma table
MOVE.L D0,A2 ; get pointer to new gamma table
TST.W GVersion(A2) ; version = 0?
BNE V8CtlBad ; => no, return error
TST.W GType(A2) ; test the hardware ID
BEQ.S V8ChangeTable ; if 0, then accept a TFB gamma table
CMP.W #drHwElsie,GType(A2) ; type = Elsie?
BNE V8CtlBad ; => no, return error
TST.W gFormulaSize(A2) ; if gType=Aurora, then check for monID in gFormulaData
BEQ.S V8ChangeTable ; if zero, then generic, so continue
MOVE.W gFormulaData(A2),D0 ; get the monitor ID this table was intended for
CMP.W saveID(A3),D0 ; is this the monitor?
BEQ.S V8ChangeTable ; yes, so do it
ADDQ #1,D0 ; was it -1?
BNE V8CtlBad ; nope, so must be wrong monitor
; if new table is different size, reallocate memory
V8ChangeTable
MOVE.L saveGammaPtr(A3),A0 ; get current gamma in A0
MOVE GFormulaSize(A2),D0 ; get size of formula in new
CMP GFormulaSize(A0),D0 ; same as current gamma table
BNE.S @GetNew ; =>no, resize pointer
MOVE GChanCnt(A2),D0 ; get number of tables in new
CMP GChanCnt(A0),D0 ; same as current gamma table?
BEQ.S @SizeOK ; => yes, data size ok
BGT.S @GetNew ; => new one is bigger, save old one
@NewSize _DisposPtr ; if new one smaller, dispose old one
CLR.L saveGammaPtr(A3) ; flag it's been disposed
@GetNew MOVE GDataCnt(A2),D0 ; get number of entries
MULU GChanCnt(A2),D0 ; multiply by number of tables
ADD GFormulaSize(A2),D0 ; add size of formula data
ADD #GFormulaData,D0 ; add gamma table header size
_NewPtr ,Sys ; and allocate a new pointer
BNE V8CtlBad ; => unable to allocate storage
MOVE.L saveGammaPtr(A3),D0 ; get old gamma table
MOVE.L A0,saveGammaPtr(A3) ; save new gamma table
TST.L D0 ; was there an old one?
BEQ.S @SizeOK ; => no, already disposed
MOVE.L D0,A0 ; else get old table
_DisposPtr ; and dispose of old gamma table
MOVE.L saveGammaPtr(A3),A0 ; get new gamma table back
; copy the gamma table header
@SizeOK MOVE GChanCnt(A2),D0 ; get number of tables
MOVE GFormulaSize(A2),D1 ; get size of formula data
MOVE gDataCnt(A2),D2 ; get number of entries
MOVE.L (A2)+,(A0)+ ; copy gamma header
MOVE.L (A2)+,(A0)+ ; which is
MOVE.L (A2)+,(A0)+ ; 12 bytes long
; copy the data
MULU D0,D2 ; multiply by number of tables
ADD D1,D2 ; add in size of formula data
SUBQ #1,D2 ; get count - 1
@NxtByte MOVE.B (A2)+,D0 ; get a byte
MOVE.B D0,(A0)+ ; move a byte
DBRA D2,@NxtByte ; => repeat for all bytes
BTST #IsDirect,GFlags(A3) ; are we in a direct mode?
BEQ.S @Out ; if not, then we're done
BSR V8DirectCLUTSet ; is so, then set up direct CLUT ramps
@Out
BRA V8CtlGood ; => return no error
;
; set up a linear gamma table. To prevent memory thrash, build this new one <7>
; the same size as the existing one (one or three channel).
;
V8LinearTab
MOVE.L saveGammaPtr(A3),A0 ; get current gamma in A2
MOVE.W GFormulaSize(A0),D0 ; get size of formula in new
MOVE.W GChanCnt(A0),D2 ; get the number of tables
SUBQ #1,D2 ; zero based, of course
ADDA #GFormulaData,A0 ; point to tables
ADDA D0,A0 ; point past monID, if present
@ChanLoop MOVE.W #$FF,D0 ; loop count within each channel
@entryLoop MOVE.B D0,(A0,D0) ; write this value out
DBRA D0,@entryLoop ; for each entry in channel
DBRA D2,@ChanLoop ; and each channel
BRA V8CtlGood ; all done
ENDWITH
V8GrayPage
;---------------------------------------------------------------------
;
; Clear the specified page in the current mode to gray
;
; A0 = Ptr to private storage
; A1 = Ptr to DCE
; A2 = Ptr to cs parameter record
; A3 = Ptr to driver privates
;
;---------------------------------------------------------------------
WITH V8VidPrivates
MOVE saveMode(A3),D1 ; D1 = mode
MOVE D1,csMode(A2) ; force current mode, just in case for ChkPage
BSR V8ChkMode ; convert mode to depth in D1
BNE V8CtlBad ; => not a valid depth
MOVE csPage(A2),D0 ; D0 = page
BNE V8CtlBad ; => not a valid page
BSR V8GrayScreen ; paint the screen gray
BTST #IsDirect,GFlags(A3) ; are we in a direct mode?
BEQ.S @Out ; if not, then we're done
BSR V8DirectCLUTSet ; is so, then set up direct CLUT ramps
@Out
BRA V8CtlGood ; => return no error
ENDWITH
V8SetGray
;---------------------------------------------------------------------
;
; Set luminance mapping on (csMode = 1) or off (csMode = 0)
;
; When luminance mapping is on, RGB values passed to setEntries are mapped
; to grayscale equivalents before they are written to the CLUT.
;
; A1 = Ptr to DCE
; A2 = Ptr to cs parameter record
;
;---------------------------------------------------------------------
WITH V8VidPrivates
MOVEQ #0,D1 ; set up for BFEXTU to point to GrayFlag
BSR.S V8SetIntCom ; call common code
BRA V8CtlGood ; all done
;
; this shared routine setup up a flag in GFlags. It takes a pointer to
; private storage in A3, and the bit field start location in D1
;
V8SetIntCom
MOVE.B csMode(A2),D0 ; get boolean
BFINS D0,GFlags(A3){D1:1} ; set flag bit
RTS ; and return
ENDWITH
V8SetInterrupt
;---------------------------------------------------------------------
;
; Enable (csMode = 0) or disable (csMode = 1) VBL interrupts
;
; As a future performance enhancement, interrupts on the card can be
; disabled or enabled from software. For instance, if the cursor is
; not on a screen, and there is nothing in the Slot Interrupt Queue
; for that device, interrupts may be disabled reducing interrupt
; overhead for the system.
;
; The slot interrupt queue element is always allocated by the Open call.
; This routine just inserts and removes it from the slot interrupt task queue.
;
; A1 = Ptr to DCE
; A2 = Ptr to cs parameter record
; A3 = Ptr to private storage
;
;---------------------------------------------------------------------
WITH VDPageInfo,SlotIntQElement,V8VidPrivates
MOVEQ #1,D1 ; set up for BFEXTU to point to IntDisFlag
BSR.S V8SetIntCom ; call common code
BNE.S V8DisableThem ; if zero, then enable
;
; this code enables interrupts and installs the interrupt handler
;
BSR.S V8EnableVGuts ; call common code
BNE V8CtlBad ; error, flag problem
BRA V8CtlGood ; and go home
;
; this code disables VBL interrupts, then removes the interrupt handler
;
V8DisableThem
BSR.S V8DisableVGuts ; jump to the disabling utility
BRA V8CtlGood ; all done
;
; the following two routines are common code shared between the Open/Close calls
; and the SetInterrupt control call
;
V8DisableVGuts
MOVE.W SR,-(SP) ; preserve the status register
MOVEQ #7,D0 ; get mask in D0
AND.B (SP),D0 ; get the interrupt level
SUBQ.B #2,D0 ;
BGE.S @OK ; if ³, then don't change
ORI.W #$0200,SR ; raise above level-2
ANDI.W #$FAFF,SR ; make it level-2
@OK
BSR V8WaitVSync ; to be safe, wait for the next VBL
MOVE.L V8,A0 ; point to the VISA base
MOVE.B #$40,V8SEnb(A0) ; set slot 0 interrupt disabled (slot 0 bit+set/clear to 0) <1.3>
MOVE (SP)+,SR ; re-enable cursor interrupts
CLR D0 ; setup slot # for _SIntRemove (slot zero!)
MOVE.L saveSQElPtr(A3),A0 ; get the SQ element pointer
_SIntRemove ; remove the interrupt handler
RTS
V8EnableVGuts ;
MOVE.L saveSQElPtr(A3),A0 ; get the queue element
LEA V8BeginIH,A2 ; save Pointer to interrupt handler
MOVE.W #SIQType,SQType(A0) ; setup queue ID
MOVE.L A2,SQAddr(A0) ; setup int routine address
MOVE.L dCtlStorage(A1),A2 ; base handle to privates
MOVE.L (A2),A2 ; pointer to privates (this must stay locked!!)
MOVE.L A2,SQParm(A0) ; pass this as the parameter
CLR.W D0 ; setup slot zero
_SIntInstall ; and do install
BNE.S @IntBad
MOVE.L A0,-(SP) ; save this for a second
MOVE.L V8,A0 ; point to the RBV base
MOVE.B #$C0,V8SEnb(A0) ; set slot 0 interrupt enabled (slot 0 bit+set/clear to 1) <1.3>
MOVE.L (SP)+,A0
MOVE D0,D0 ; clear z-bit for good result
@IntBad RTS ; return home (if bad, z-bit is set above, so just leave) <8>
ENDWITH
V8DirectSetEntries
;---------------------------------------------------------------------
;
; Change the CLUT in a direct mode.
;
; A1 = Ptr to DCE
; A2 = Ptr to cs parameter record
; A3 = Ptr to private storage
;
; This routine allows knowledgeable programs modify the contents
; of the CLUT in direct modes (usually for limited color previewing).
; It takes the same parameter block as SetEntries, but SetEntries
; intentionally does not operate when the card is in a direct pixMode.
; This routine takes the same data and operates ONLY when in direct
; modes. It calls the same SetEntries guts as the regular routine.
;
;---------------------------------------------------------------------
BTST #IsDirect,GFlags(A3) ; are we in a direct mode?
BEQ V8CtlBad ; error if not
CMP.W #31,csCount(A2) ; only 32 changeable positions in direct mode
BGT V8CtlBad ; error if not
BRA SEGuts ; jump to SetEntries internals if it's OK
V8SetDefaultMode
;---------------------------------------------------------------------
;
; Write the card default mode into slot pRAM.
;
; A1 = Ptr to DCE
; A2 = Ptr to cs parameter record
; A3 = Ptr to private storage
;
; This routine is called by Monitors when somebody selects an alternate
; video mode family in the Options dialog.
;
;---------------------------------------------------------------------
WITH spBlock,V8VidPrivates
;
; set up a slot parameter block on the stack
;
SUBA #spBlockSize,SP ; make an slot parameter block on stack
MOVE.L SP,A0 ; get pointer to parm block now
MOVE.B dCtlSlot(A1),spSlot(A0) ; put slot in pBlock
CLR.B spExtDev(A0) ; external device = 0
;
; read the slot pRAM to determine what the currently saved mode is. The first
; word is the board ID, followed by the default screen depth. Aurora keeps the video sRsrc
; spID in VendorUse2. This guy DOESN'T check to make sure that the mode being set if
; valid for the display, but that's OK since Monitors can't see the other modes anyway
;
SUBA #SizesPRAMRec,SP ; allocate block for pRAM record
MOVE.L SP,spResult(A0) ; point to it
_sReadPRAMRec ; read it
;
; The parameter list id (identifying the screen depth) in 2(SP) will still be valid.
;
; It is very important that Monitors (or someone) invalidate and setup the screen resource
; if this call is exercised. Monitors needs to verify (and potentially re-write to pRAM)
; the proper screen depth in the new world.
;
MOVE.B csMode(A2),3(SP) ; write the mode into pRAM buffer
MOVE.L SP,spsPointer(A0) ; set up parameter block
_SPutPRAMRec ; write the new record out
ADDA #SizesPRAMRec+spBlockSize,SP ; deallocate buffer
BRA V8CtlGood
ENDWITH
V8Goodbye
;---------------------------------------------------------------------
;
; Special routine to entire CLUT to 50% gray.
;
; Since the VISA can't suppress video sync, we gray out the CLUT on
; restart to hide any mode change artifacts. We could have buried this
; code in the Shutdown.a, but it's better to collect all the device
; specific code here.
;
; No parameters
;
;---------------------------------------------------------------------
; raise to interrupt level 2 the new, good way
MOVE.W SR,-(SP) ; preserve the status register
MOVEQ #7,D2 ; get mask in D0
AND.B (SP),D2 ; get the interrupt level
SUBQ.B #2,D2 ;
BGE.S @OK ; if ³, then don't change
ORI.W #$0200,SR ; raise above level-2
ANDI.W #$FAFF,SR ; make it level-2
@OK
; blank everything
WITH ProductInfo,DecoderInfo,VideoInfo
MOVE.L UnivInfoPtr,A0 ; get a pointer to universal data
ADD.L DecoderInfoPtr(A0),A0 ; point to the base address table
MOVE.L VDACAddr(A0),A0 ; get chip base address
ADDA #V8DACwDataReg,A0 ; point to data register
CLR.B V8DACwAddReg-V8DACwDataReg(A0) ; start at the beginning of CLUT, 4-bit mode
ENDWITH
MOVE.B #$7F,D3 ; get a 50% value
MOVE #($256*3)-1,D2 ; get count <8>
@Repeat MOVE.B D3,(A0) ; put component (red=green=blue) <8>
DBRA D2,@Repeat ;
MOVE (SP)+,SR ; restore the status reg
BRA V8CtlGood
**********************************************************************
*
* VideoClose releases the device's private storage and removes the
* interrupt handler.
*
*
* Entry: A0 = param block pointer
* A1 = DCE pointer
*
* Other: A2 = temporary DCE pointer copy
*
**********************************************************************
V8VidClose
WITH V8VidPrivates
MOVE.L dCtlStorage(A1),A3
MOVE.L (A3),A3 ; get pointer to private storage
BSR V8DisableVGuts ; call utility to deactivate interrupts
MOVE.L saveSQElPtr(A3),A0 ; get the slot interrupt queue element ptr
_DisposPtr
MOVE.L saveGamDispPtr(A3),A0 ; get pointer to gamma table block
_DisposPtr ; and dispose it
MOVE.L dCtlStorage(A1),A0 ; Dispose of the private storage
_DisposHandle ;
MOVEQ #0,D0 ; no error
RTS ; and return
ENDWITH
**********************************************************************
*
* Video Driver Status Call Handler. Right now there are nine calls:
*
* (0) Error
* (1) Error
* (2) GetMode
* (3) GetEntries
* (4) GetPage
* (5) GetPageBase
* (6) GetGray
* (7) GetInterrupt
* (8) GetGamma
* (9) GetDefaultMode
*
* Entry: A0 = param block
* A1 = DCE pointer
* Uses: A2 = cs parameters
* A3 = pointer to private storage
* D0-D3 = scratch (don't need to be preserved)
*
* Exit: D0 = error code
*
**********************************************************************
V8VidStatus
MOVE.L A0,-(SP) ; save a register
MOVE.W csCode(A0),D0 ; get the opCode
MOVE.L csParam(A0),A2 ; A2 <- Ptr to control parameters
MOVE.L dCtlStorage(A1),A3
MOVE.L (A3),A3 ; get pointer to private storage
CMP.W #9,D0 ;IF csCode NOT IN [0..9] THEN
BHI.S V8StatBad ; Error, csCode out of bounds.
LSL.W #1,D0 ;Adjust csCode to be an index into the table.
MOVE.W V8StatJumpTbl(PC,D0.W),D0 ;Get the relative offset to the routine.
JMP V8StatJumpTbl(PC,D0.W) ;GOTO the proper routine.
V8StatJumpTbl
DC.W V8StatBad-V8StatJumpTbl ;$00 => Error
DC.W V8StatBad-V8StatJumpTbl ;$01 => Error
DC.W V8GetMode-V8StatJumpTbl ;$02 => GetMode
DC.W V8GetEntries-V8StatJumpTbl ;$03 => GetEntries
DC.W V8GetPage-V8StatJumpTbl ;$04 => GetPage
DC.W V8GetPageBase-V8StatJumpTbl ;$05 => GetPageBase
DC.W V8GetGray-V8StatJumpTbl ;$06 => GetGray
DC.W V8GetInterrupt-V8StatJumpTbl ;$07 => GetInterrupt
DC.W V8GetGamma-V8StatJumpTbl ;$08 => GetGamma
DC.W V8GetDefaultMode-V8StatJumpTbl ;$09 => GetDefaultMode
V8StatBad MOVEQ #statusErr,D0 ; else say we don't do this one
BRA.S V8StatDone ; and return
V8StatGood MOVEQ #noErr,D0 ; return no error
V8StatDone MOVE.L (SP)+,A0 ; restore registers.
BRA V8ExitDrvr
V8GetMode
;---------------------------------------------------------------------
;
; Return the current mode
;
; Inputs : A2 = pointer to csParams
; A3 = pointer to private storage
;
;---------------------------------------------------------------------
WITH V8VidPrivates
MOVE.W saveMode(A3),csMode(A2) ; return the mode
MOVE.W #0,csPage(A2) ; return the page number (always 0)
MOVE.L saveBaseAddr(A3),csBaseAddr(A2) ; and the base address
BRA.S V8StatGood ; => return no error
ENDWITH
V8GetEntries
;---------------------------------------------------------------------
;
; Read the current contents of the CLUT. These values were gamma corrected
; when they were set (by SetEntries), so they may not match the source
; cSpec array.
;
; Inputs : A2 = pointer to csParams
;
;---------------------------------------------------------------------
MOVE.L csTable(A2),D0 ; Check for a nil pointer
BEQ V8StatBad
MOVEM.L A1/A3-A4/D2-D5,-(SP) ; save work registers
MOVE.L D0,A4 ; A4 <- pointer to table
; get the index range in D3
WITH CLUTDatRec
LEA V8CLUTTbl,A0 ; point to CLUT parameters
MOVE.W saveMode(A3),D0 ; get the screen depth ID
SUB #FIrstVidMode,D0 ; make it an index
MOVE.B Range(A0,D0*4),D3 ; get the maximum CLUT index
MOVE.B Start(A0,D0*4),D4 ; get the starting position
MOVE.W Skip(A0,D0*4),D5 ; get the inter-entry skip value
ENDWITH
MOVE.W csCount(A2),D0 ; get the count
MOVE.W D0,D2 ; make a copy of the count
TST.W csStart(A2) ; is it index or sequence mode?
BMI.S V8GECom ; if index, then continue
MOVE.W D0,D1 ; copy count into another loop counter
ADD.W csStart(A2),D2 ; get last index
@1
MOVE.W D2,value(A4,D1*8) ; write the index into the table
SUBQ #1,D2 ; decrease index
DBRA D1,@1 ; for all indices
V8GECom
MOVE.L saveVDACBase(A3),A1 ; get base address of CLUT
ADDQ #v8DACwDataReg,A1 ; read and write data are the same register
MOVE.W SR,-(SP) ; preserve the status register
MOVEQ #7,D1 ; get mask in D0
AND.B (SP),D1 ; get the interrupt level
SUBQ.B #2,D1 ;
BGE.S @OK ; if ³, then don't change
ORI.W #$0200,SR ; raise above level-2
ANDI.W #$FAFF,SR ; make it level-2
@OK
@Repeat
MOVE.W (A4)+,D1 ; get the value field for from the next colorspec in D1
CMP.B D3,D1 ; Is this request in range?
BHI.S @Until ; if hi, then no, the request is invalid
MULU D5,D1 ; multiply index by skip value
ADD.B D4,D1 ; add starting offset
MOVE.B D1,V8DACrAddReg-V8DACwDataReg(A1) ; set the CLUT to read from this addr <7><daf,jj>
MOVEQ #3-1,D6 ; loop counter for # of channels
@CLUTLoop MOVE.B (A1),D1 ; get value
MOVE.B D1,(A4)+ ; byte->word in the dest cSpecArray
MOVE.B D1,(A4)+ ;
DBRA D6,@CLUTLoop ; for each channel
@Until DBRA D0,@Repeat ; and loop until done
MOVE (SP)+,SR ; restore the status register
MOVEM.L (SP)+,A1/A3-A4/D2-D5 ; restore work register
BRA V8StatGood ; => return no error
V8GetPage
;---------------------------------------------------------------------
;
; Return the number of pages in the specified mode. It's pretty simple;
; every mode has only one page. We do check if it's valid, however.
;
;---------------------------------------------------------------------
WITH V8VidPrivates
MOVE csMode(A2),D1 ; get the mode
MOVE D1,D2 ; keep a copy
BSR.S V8ChkMode ; is this mode OK?
BGT V8StatBad ; => not a valid mode
MOVE.W #1,csPage(A2) ; return page count
BRA V8StatGood ; => return no error
ENDWITH
V8GetPageBase
;---------------------------------------------------------------------
;
; Return the base address for the specified page in the current mode
;
;---------------------------------------------------------------------
WITH V8VidPrivates
TST.W csPage(A2) ; are we returning page zero info?
BNE V8StatBad ; only page 0 is valid
MOVE.L saveBaseAddr(A3),csBaseAddr(A2) ; return screen base address
BRA V8StatGood ; => return no error
ENDWITH
V8GetGray
;---------------------------------------------------------------------
;
; Return a boolean, set true if luminance mapping is on
;
;---------------------------------------------------------------------
WITH V8VidPrivates
MOVEQ #0,D1 ; set up for BFEXTU
V8GetFlagCom
BFEXTU GFlags(A3){D1:1},D0 ; get the state of flag
MOVE.B D0,csMode(A2) ; return value
BRA V8StatGood ; => and return
ENDWITH
V8GetInterrupt
;---------------------------------------------------------------------
;
; Return a boolean in csMode, set true if VBL interrupts are disabled
;
;---------------------------------------------------------------------
WITH V8VidPrivates
MOVEQ #1,D1 ; set up BFEXTU to point at IntDisFlag
BRA.S V8GetFlagCom ; and use common code
ENDWITH
V8GetGamma
;---------------------------------------------------------------------
;
; Return the pointer to the current gamma table
;
;---------------------------------------------------------------------
WITH V8VidPrivates
MOVE.L saveGammaPtr(A3),csGTable(A2) ; return the pointer to the structure
BRA V8StatGood ; and return a good result
ENDWITH
V8GetDefaultMode
;---------------------------------------------------------------------
;
; Read the card default mode from slot pRAM.
;
; A1 = Ptr to DCE
; A2 = Ptr to cs parameter record
; A3 = Ptr to private storage
;
;---------------------------------------------------------------------
WITH spBlock,V8VidPrivates
;
; set up a slot parameter block on the stack
;
SUBA #spBlockSize,SP ; make an slot parameter block on stack
MOVE.L SP,A0 ; get pointer to parm block now
MOVE.B dCtlSlot(A1),spSlot(A0) ; put slot in pBlock
CLR.B spExtDev(A0) ; external device = 0
;
; read the slot pRAM to determine what the currently saved mode is. The first
; byte is the board ID, followed by the default mode. Aurora keeps the last
; selected video sRsrc spID in VendorUse2.
;
SUBA #SizesPRAMRec,SP ; allocate block for pRAM record
MOVE.L SP,spResult(A0) ; point to it
_sReadPRAMRec ; read it
MOVE.B 3(SP),csMode(A2) ; return the result
ADDA #SizesPRAMRec+spBlockSize,SP ; release buffer
BRA V8StatGood ;
ENDWITH
;---------------------------------------------------------------------
;
; Exit from control or Status.
;
;---------------------------------------------------------------------
V8ExitDrvr BTST #NoQueueBit,ioTrap(A0) ; no queue bit set?
BEQ.S V8GoIODone ; => no, not immediate
RTS ; otherwise, it was an immediate call
V8GoIODone MOVE.L JIODone,A0 ; get the IODone address
JMP (A0) ; invoke it
;=====================================================================
;
; Utilities
;
;=====================================================================
;---------------------------------------------------------------------
;
; ChkMode
;
; Verifies the requested mode is legal. Converts spID in D1 into
; zero-based mode number since lots of people want it that way.
;
; <-> D1: Mode
; -> A3: Pointer to driver privates
;
; All registers preserved
;
; Returns EQ if mode is valid.
;
V8ChkMode
WITH V8VidPrivates
MOVEM.L D0/D2-D3,-(SP) ; save regs
SUB.W #FirstVidMode,D1 ; make modeID zero based
MOVE.B saveSlotID(A3),D0 ; get the slot manager mode ID
BCLR #4,D0 ; if this bit is on, either A// mode or no vRAM
BEQ.S @testDepth ; if not, then do simple test
; if we are in A// emulation mode, then warp D0 to make the monitor look like a High-Res rather than a Rubik.
; We do this because the V8 has one less mode available when in A// mode (8-bit only with 512K vRAM, 4-bit
; with 256K vRAM). Note that the A// mode bit was flipped off in a test above!
@adjA2Em
BSET #2,D0 ; transmogrify A// Rubik into High-Res
; test for the greatest available depth. We'll set this up in D2 and compare to D1
@testDepth
MOVE #ThirdVidMode-FirstVidMode,D2 ; everybody at this point can do 4-bit mode
;+++ BTST #2,D0 ; Rubiks have one more possible mode over Hi-Res & VGA
MOVE.B D0,D3 ; copy the spID
AND.B #5,D3 ; Rubik = 010, HR=110, VGA=011, so we test for
; Rubik by making sure bits 0 and 2 are clear
BNE.S @vRAMTest ; if not, then continue
ADDQ #1,D2 ; reflect the additional mode
@vRAMTest
BTST #3,D0 ; 512K machines get one additional mode in all display flavors
BNE.S @ShowDown ;
ADDQ #1,D2 ; reflect the additional mode
@ShowDown CMP.B D2,D1 ; see if we're OK
BGT.S @ModeBad ; if D2>D1, it's an illegal mode
@ModeOK CMP.W D1,D1 ; get EQ
@ModeBad MOVEM.L (SP)+,D0/D2-D3 ; restore trashed regs
@ModeOut RTS ; EQ if valid depth
ENDWITH
;---------------------------------------------------------------------
;
; Wait for vertical blanking. Interrupts are raised to level-2 around
; this routine.
;
; A1 = DCE POINTER
;---------------------------------------------------------------------
V8WaitVSync
MOVE.L A0,-(SP) ; save work registers <8>
MOVE.L D0,-(SP) ; (two MOVEs are faster than a MOVEM) <8>
MOVE.L V8,A0 ; point to VISA
MOVE.B #$40,V8SInt(A0) ; clear the interrupt, just in case,
; since the intHndlr can't get
; called here
ADDA #V8SInt,A0 ; point to interrupt register
@0 MOVE.B (A0),D0 ; Read the Vert-Sync state
BTST #6,D0 ;
BNE.S @0 ; Repeat until it goes low
;
MOVE.L (SP)+,D0 ; restore work registers <8>
MOVE.L (SP)+,A0 ; (two MOVES are faster than a MOVEM) <8>
RTS
V8SetDepth
;---------------------------------------------------------------------
;
; SetDepth sets the V8 frame buffer depth, and returns the
; frame buffer base in driver privates
;
; D0 contains the page number
; D1 contains the spID of the depth - $80 (the zero based mode ID)
; A2 = parameter block pointer
; A3 = dCtlStorage pointer
;
; Preserves all registers
;
;---------------------------------------------------------------------
WITH V8VidPrivates
MOVEM.L D2/D3/A0/A1,-(SP) ; save regs we are using <1.8>
MOVE.B D1,D3 ; make a copy of the depth value
; Test for Apple // mode. If it is this mode, then we need to set the V8 to the next greater
; depth.
MOVE.B saveSlotId(A3),D2 ; get spID in a register
AND.B #$F7,D2 ; don't look at the vRAM size bit
CMP.B #sRsrc_Vid_V8_A2Ema,D2 ; compare to 512K Apple // mode
BNE.S @0
ADDQ #1,D3 ; bump the depth up by one
@0
MOVE.L V8,A0 ; point to Elsie base
MOVE.B V8MonP(A0),D2 ; get the V8 state
AND.B #ClrDepthBitsMask,D2 ; clear the lo-3 bits (depth control)
OR.B D3,D2 ; turn on the depth bits
MOVE.B D2,V8MonP(A0) ; set the mode
; set up the Ariel
MOVE.L saveVDACBase(A3),A0 ; point to the DAC
MOVE.B V8DACrCntlReg(A0),D2 ; get the Ariel control register value
AND.B #ClrDepthBitsMask,D2 ; clear the depth bits
OR.B D1,D2 ; set the new depth
MOVE.B D2,V8DACwCntlReg(A0) ; set the new control register value
@CalcBase
MOVE.W csMode(A2),saveMode(A3) ; save mode number
CMP.W #FifthVidMode,saveMode(A3) ; was it 16-bit mode?
BNE.S @BitOff ; no, so turn flag off
BSET #IsDirect,GFlags(A3) ; turn on bit
BRA.S @Out ;
@BitOff
BCLR #IsDirect,GFlags(A3) ; turn off bit
@Out
MOVEM.L (SP)+,D2/D3/A0/A1 ; restore all regs <1.8>
RTS ; return
ENDWITH
V8GrayScreen
;---------------------------------------------------------------------
;
; Fill the screen with a 50% dithered gray pattern. To have gotten here
; we must have had a valid display connected, so there are not tests
; for inactive displays here.
;
; D1 = spID of screen depth - FirstVidMode
; A3 = driver private storage
;
; All registers are preserved
;
WITH V8VidPrivates
MOVEM.L D0-D3/A0-A1,-(SP) ; save all registers
LEA V8Pats,A1 ; get a pointer to the pattern table
MOVE.L (A1,D1*4),D3 ; D3 = the proper pattern
MOVEQ.L #$17,D0 ; make a mask for pertinent bits
AND.B saveSlotId(A3),D0 ; get the monitor+upper option bit
CMP.B #sRsrc_Vid_V8_GSa**$17,D0 ; is it a standard Rubik?
BNE.S @0 ; if not, continue
LEA V8GSParms,A1 ;
BRA.S @Blast2
@0 CMP.B #sRsrc_Vid_V8_A2Ema**$17,D0 ; is it a Rubik in A// mode?
BNE.S @1 ; if not, continue
LEA V8A2Parms,A1 ;
BRA.S @Blast2
; if it's none of the above it's either a High-Res w/vRAM or a VGA which use the same parameters
@1 LEA V8HRvRAMParms,A1 ;
@Blast2
BTST #3,saveSlotID(A3) ; if this bit is cleared, then 512K vRAM
BNE.S @ItsOK ; if bit set, then 256K vRAM so parm addr is OK
; ¥¥¥!!!¥¥¥ ADDA #LongParmShift,A1 ; advance A1 to 512K parameters
ADDA #58,A1 ; advance A1 to 512K parameters
@ItsOK
MOVE.L saveBaseAddr(A3),A0 ; get the frame buffer base address
MOVE.W (A1)+,D0 ; get the # of rows
@NxtRow
MOVE.W (A1,D1*4),D2 ; get the # of longs/row
@NxtLong MOVE.L D3,(A0)+ ; write gray
DBRA D2,@NxtLong ; for each scanline
ADD.W 2(A1,D1*4),A0 ; add the skip factor for each line
NOT.L D3 ; invert pattern on next row
DBRA D0,@NxtRow ; for each row
MOVEM.L (SP)+,D0-D3/A0-A1 ; restore registers
RTS
V8Pats DC.L OneBitGray,TwoBitGray,FourBitGray,EightBitGray,SixteenBitGray
V8DirectCLUTSet
;---------------------------------------------------------------------
;
; V8DirectCLUTSet writes gamma-corrected ascending grayscale ramps into
; the CLUT
;
; A3 = dCtlStorage pointer
;
; Preserves all registers
;
;---------------------------------------------------------------------
MOVEM.L D0-D2/A0/A4-A6,-(SP) ; save a register
MOVE.L saveGammaPtr(A3),A0 ; get pointer to gamma data structure
MOVE.W GFormulaSize(A0),D0 ; get the size of formula data
LEA GFormulaData(A0),A4 ; point to formula data
ADD D0,A4 ; red correction table starts here
MOVE.L A4,A5 ; get default pointer to green data
MOVE.L A4,A6 ; get default pointer to blue data
CMP #1,GChanCnt(A0) ; if only only one table, we're set
BEQ.S @OneTbl ; => just one table
MOVE GDataWidth(A0),D1 ; get width of each entry in bits
ADD #7,D1 ; round to nearest byte
LSR #3,D1 ; get bytes per entry
MULU GDataCnt(A0),D1 ; get size of table in bytes
ADDA D1,A5 ; calc base of green
ASL #1,D0 ; mulu times 2
ADDA D1,A6 ; calc base of blue
@OneTbl
MOVE.L saveVDACBase(A3),A0 ; point to the hardware
ADDA #V8DACwDataReg,A0 ; point to data register
CLR.B V8DACwAddReg-V8DACwDataReg(A0) ; start at the beginning of CLUT
MOVE.W #$1F,D2 ; set up counter
MOVE.W SR,-(SP) ; preserve the status register
MOVEQ #7,D0 ; get mask in D0
AND.B (SP),D0 ; get the interrupt level
SUBQ.B #2,D0 ;
BGE.S @OK ; if ³, then don't change
ORI.W #$0200,SR ; raise above level-2
ANDI.W #$FAFF,SR ; make it level-2
@OK
; write an incrementing grayscale ramp
MOVEQ #0,D0 ; set up ramp start
@Repeat
MOVE.L D0,D1 ; copy the position pointer
SWAP D1 ; get the ÒintegerÓ part
MOVE.B (A4,D1),(A0) ; put red
MOVE.B (A5,D1),(A0) ; put green
MOVE.B (A6,D1),(A0) ; put blue
ADD.L #$00083900,D0 ; add 8 and 7/31 to the number
DBRA D2,@Repeat ;
MOVE (SP)+,SR ; restore the status reg
MOVEM.L (SP)+,D0-D2/A0/A4-A6 ; restore saved registers
RTS
;
; some simple screen size parameters. The first number is the number of scanlines to
; erase for each mode. The next numbers are in pairs. The first element of each pair is the number
; of longs to gray per scanline. The second element is the skip factor (number of bytes to skip
; to get to beginning of the next scanline). The second set of numbers are the same info for the
; 512K vRAM version (which has 1024 rowbytes rather than 512). There are more modes in the second
; set, but the code in V8GrayScreen knows how to deal with it
;
V8GSParms DC.W defmBounds_BGS-1
DC.W (((defmBounds_RGS*1)/8)/4)-1,(V8_512_RB-((defmBounds_RGS*1)/8))
DC.W (((defmBounds_RGS*2)/8)/4)-1,(V8_512_RB-((defmBounds_RGS*2)/8))
DC.W (((defmBounds_RGS*4)/8)/4)-1,(V8_512_RB-((defmBounds_RGS*4)/8))
DC.W (((defmBounds_RGS*8)/8)/4)-1,(V8_512_RB-((defmBounds_RGS*8)/8))
DC.W 0,0 ; pad out table
V8HRvRAMParms DC.W defmBounds_BHR-1
DC.W (((defmBounds_RHR*1)/8)/4)-1,(V8_512_RB-((defmBounds_RHR*1)/8))
DC.W (((defmBounds_RHR*2)/8)/4)-1,(V8_512_RB-((defmBounds_RHR*2)/8))
DC.W (((defmBounds_RHR*4)/8)/4)-1,(V8_512_RB-((defmBounds_RHR*4)/8))
DC.W 0,0 ; pad out table
V8A2Parms DC.W defmBounds_BA2Em-1
DC.W (((defmBounds_RA2Em*1)/8)/4)-1,(V8_512_RB-((defmBounds_RA2Em*1)/8))
DC.W (((defmBounds_RA2Em*2)/8)/4)-1,(V8_512_RB-((defmBounds_RA2Em*2)/8))
DC.W (((defmBounds_RA2Em*4)/8)/4)-1,(V8_512_RB-((defmBounds_RA2Em*4)/8))
DC.W 0,0 ; pad out table
V8GSParms_1024 DC.W defmBounds_BGS-1
DC.W (((defmBounds_RGS*1)/8)/4)-1,(V8_1024_RB-((defmBounds_RGS*1)/8))
DC.W (((defmBounds_RGS*2)/8)/4)-1,(V8_1024_RB-((defmBounds_RGS*2)/8))
DC.W (((defmBounds_RGS*4)/8)/4)-1,(V8_1024_RB-((defmBounds_RGS*4)/8))
DC.W (((defmBounds_RGS*8)/8)/4)-1,(V8_1024_RB-((defmBounds_RGS*8)/8))
DC.W (((defmBounds_RGS*16)/8)/4)-1,(V8_1024_RB-((defmBounds_RGS*16)/8))
V8HRvRAMParms_1024 DC.W defmBounds_BHR-1
DC.W (((defmBounds_RHR*1)/8)/4)-1,(V8_1024_RB-((defmBounds_RHR*1)/8))
DC.W (((defmBounds_RHR*2)/8)/4)-1,(V8_1024_RB-((defmBounds_RHR*2)/8))
DC.W (((defmBounds_RHR*4)/8)/4)-1,(V8_1024_RB-((defmBounds_RHR*4)/8))
DC.W (((defmBounds_RHR*8)/8)/4)-1,(V8_1024_RB-((defmBounds_RHR*8)/8))
V8A2Parms_1024 DC.W defmBounds_BA2Em-1
DC.W (((defmBounds_RA2Em*1)/8)/4)-1,(V8_1024_RB-((defmBounds_RA2Em*1)/8))
DC.W (((defmBounds_RA2Em*2)/8)/4)-1,(V8_1024_RB-((defmBounds_RA2Em*2)/8))
DC.W (((defmBounds_RA2Em*4)/8)/4)-1,(V8_1024_RB-((defmBounds_RA2Em*4)/8))
DC.W (((defmBounds_RA2Em*8)/8)/4)-1,(V8_1024_RB-((defmBounds_RA2Em*8)/8))
V8HRParms DC.W defmBounds_BHR-1
DC.W (((defmBounds_RHR*1)/8)/4)-1,(OBMHRRB-((defmBounds_RHR*1)/8))
LongParmShift EQU V8GSParms_1024-V8GSParms
ENDWITH
;-------------------------------------------------------------
; The Interrupt handler for the Elsie Built-In Video
;-------------------------------------------------------------
; On entry A1 contains the pointer to the driver's private storage
; D0-D3/A0-A3 have been preserved.
V8BeginIH
MOVE.L V8,A0 ; point to the VISA chip
MOVE.B #$40,V8SInt(A0) ; clear the interrupt
CLR.W D0 ; set slot zero in D0
MOVE.L JVBLTask,A0 ; call the VBL task manager
JSR (A0) ; with slot # in D0
@Done
MOVEQ #1,D0 ; signal that int was serviced
RTS ; and return to caller
END