;*DDK*************************************************************************/
;
; COPYRIGHT (C) Microsoft Corporation, 1989
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
        PAGE    60,132
        TITLE   VDHIOPL.ASM -- Video Device Handler Ring 2 Routines
;/*****************************************************************************
;*
;* SOURCE FILE NAME = VDHIOPL.ASM
;*
;* DESCRIPTIVE NAME = Video Device Handler Ring 2 Routines
;*
;*
;* VERSION      V2.0
;*
;* DATE
;*
;* DESCRIPTION
;*
;* FUNCTIONS
;*
;*
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   12/06/92              D57994 Reset SEQ 0 if SEQUENCER_CMD before setting regs.
;*   12/12/92                     Create ExecCommands routine
;*   01/29/93                     Add Diamond Speedstar 24x support
;*   02/10/93                     Add wait() procesing to ExecCommands
;*   02/17/93                     Re-arrange ExecCommands to avoid trashing stack
;*   03/03/93                     Change WAIT command to use msec counter
;*   03/10/93                     Fix supported Diamond SpeedStar support.
;*   03/17/93                     OEMINFO structure changed.
;*   09/16/93              D73553 Save ATC registers.
;*   06/02/93             F69306  Add support for S3
;*   06/02/93             F69306  Hi-color support for Diamond
;*   06/02/93                     0 reserved for ending delimiter
;*   07/12/93                     Add Diamond clock support for 1280
;*   10/06/93              72687  Add Number Nine clock support for S3
;*   10/21/93              74890  Use hi-color entries in ext clock tbl for 24bpp
;*   10/21/93              75044  Remove YEE04 fix
;*   11/01/93             D75458  Merge r206v, r205, r206, r207 S3 code
;*   02/17/94             D78589  72Hz Refresh Rate for Diamond: Clock Index
;*****************************************************************************/

.286p

;/*
;**  Include files
;*/

        include struc.inc               ; Structured assembly macros
        include vdh.inc                 ; Definitions
        include vdhequ.inc              ; OEMFlags bit definitions
        include svgatype.inc            ;           
        include svgadefs.inc            ;               ;          

        extrn   _OEMFlags:word          ; OEM specific features
        extrn   _SVGAHardware:qword     ; SVGA Info           
        extrn   _OEMHardware:qword      ; OEM Manufacturer Info           
        extrn   _SVGAMonitorType:word   ;           
        extrn   _GlobalInfoSeg:word     ;           

        PUBLIC  _HardwareColor
        PUBLIC  SETMAPMASK
        PUBLIC  ACCESSCLUT
        PUBLIC  ACCESSHARDWARE
        PUBLIC  ACCESSREGISTER
        PUBLIC  ACCESSVIDEOENABLE
        PUBLIC  ACCESSFONT
        PUBLIC  EXECCOMMANDS                            ;          

        PUBLIC  SETDIAMONDCLK           ;          
        PUBLIC  Prog_Clock              ;          
        PUBLIC  Send_Serial             ;          
        PUBLIC  GetCRTCAddr             ;          
        PUBLIC  GetDACTypeRtn           ;          
        PUBLIC  SetDACCMDReg            ;          

        PUBLIC  SETNUMBER9CLK           ;            ;          
        PUBLIC  Set_Frequency
        PUBLIC  New_Refresh_Selected


IF DEBUG
        PUBLIC  DPRINTF
ENDIF


R2SEG   SEGMENT PARA PUBLIC 'CODE'
        ASSUME  CS: R2SEG, DS: DGROUP

.386p

;/*************************************************************************
;*
;* FUNCTION NAME = ExecCommands                 (this function            
;*
;* DESCRIPTION   = Execute commands from PMI-file.
;*
;*                 ENTRY POINT: ExecCommands
;*                   LINKAGE:   CALL FAR
;*
;* INPUT         = far pointer to HWCOMMAND structure list
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;* Commands handled:
;*
;*        INB(<Variable>, <IO Port>);
;*        OUTB(<IO Port>, <data byte>);
;*        INW(<Variable>, <IO Port>);
;*        OUTW(<IO Port>, <data word>);
;*        BINB(<no of values>, <start index>, <Index Port>, <Data Port>);
;*        BOUTB(<no of values>, <start index>, <Index Port>, <Data Port>);
;*        RMWB(<Index Port>, <Data Port>, <index>, <AND mask>, <OR mask>);
;*        RMWW(<Read Port>, <Write Port>, <AND mask>, <OR mask>);
;*        WAIT(<status port>, <test mask>, <count>, <timeout>, <0|1>);
;*        Register operations....
;*
;*
;* Notes: It may be tempting to 'optimise' the RMW portion, but some hardware
;*        needs the index register to be set up on each access.
;*
;*        No sequencer resets are coded around sequencer register IO. It is
;*        assumed that if required this is part of the stream of commands.
;*
;**************************************************************************/

                ASSUME  ES:NOTHING

TOTALREGS       EQU     256                     ; 

R2DATA          SEGMENT
VarRegs         DW      TOTALREGS DUP(0)
org_5c          DB      0                       ;           ;          
R2DATA          ENDS

ExecCommands    PROC    FAR

                enter   0, 0                    ; 
                push    ds                      ; 
                push    es                      ; 
                pusha                           ; 
                cld                             ; 

                lds     si,dword ptr [bp+6]     ;Get pointer to list
                mov     ax,ds                   ;Check pointer
                or      ax,si                   ;Is it NULL?
                jz      short ExecCmdExit       ;afraid so...

                mov     ax,DGROUP               ;Set up ES to VarRegs selector
                mov     es,ax                   ; 
                mov     di,offset R2DATA:VarRegs;initialise these
                xor     ax,ax                   ;with zeros
                mov     cx,TOTALREGS            ; 
                rep     stosw                   ;clear out

                                                ;DS:SI -> Command list
                                                ;ES:?? -> Pseudo registers
                ALIGN   4
ExecCmdTop:
                mov     bx,[si].usCommand       ;Get next command to execute
                cmp     bx,PMICMD_MAX           ;Check its within range
                ja      short ExecCmdNext       ;Ignore it and continue
                test    [si].usFlags,PMIFLAG_NOOP
                jnz     short ExecCmdNext       ;Ignore it and continue
                mov     ax,[si].usStartReg      ;AX first index
                mov     di,ax                   ; 
                shl     di,1                    ;make word index
                lea     di,VarRegs[di]          ;Get pointer to data
                mov     dx,[si].usIndexPort     ;At least the first port
                shl     bx,1                    ; 
                jmp     cs:[bx + ExecTable]     ;dispatch another command

                ALIGN   4
ExecCmdNext:                                    ;Handle the next command
                mov     si,[si].pNextCmd        ;get next pointer
                or      si,si                   ;NULL => end of list
                jnz     ExecCmdTop              ;more to do

ExecCmdExit:
                popa
                pop     es
                pop     ds
                leave                           ; 
                ret     4                       ; 

IF DEBUG
                PUBLIC  ExecNONE                ;          
                PUBLIC  ExecINB
                PUBLIC  ExecOUTB
                PUBLIC  ExecINW
                PUBLIC  ExecOUTW
                PUBLIC  ExecBINB
                PUBLIC  ExecBOUTB
                PUBLIC  ExecRMWB
                PUBLIC  ExecRMWW
                PUBLIC  ExecREGOP
                PUBLIC  ExecListUpdate
                PUBLIC  ExecWAIT
ENDIF

                EVEN
ExecTable       dw      ExecNONE                ;PMICMD_NONE                  ;          
                dw      ExecINB                 ;PMICMD_INB
                dw      ExecOUTB                ;PMICMD_OUTB
                dw      ExecINW                 ;PMICMD_INW
                dw      ExecOUTW                ;PMICMD_OUTW
                dw      ExecBINB                ;PMICMD_BINB
                dw      ExecBOUTB               ;PMICMD_BOUTB
                dw      ExecRMWB                ;PMICMD_RMWB
                dw      ExecRMWW                ;PMICMD_RMWW
                dw      ExecREGOP               ;PMICMD_REGOP
                dw      ExecWAIT                ;PMICMD_WAIT


                ALIGN   4
ExecNONE:                                       ;================ PMICMD_NONE             ;          
                jmp     ExecCmdNext             ;look for more to do
ExecINB:                                        ;================ PMICMD_INB
                in      al,dx                   ; 
                xor     ah,ah                   ; 
                mov     es:[di],ax              ; 
                jmp     ExecCmdNext             ;look for more to do


                ALIGN   4
ExecOUTB:                                       ;================ PMICMD_OUTB
                test    [si].usFlags,PMIFLAG_REGISTER
                jnz     short @F                ; 
                mov     al,byte ptr [si].usData ; 
                out     dx,al                   ;write byte to port
                jmp     ExecCmdNext             ;look for more to do
@@:             mov     al,es:[di]              ; 
                out     dx,al                   ; 
                jmp     ExecCmdNext             ;look for more to do


                ALIGN   4
ExecINW:                                        ;================ PMICMD_INW
                in      ax,dx                   ; 
                mov     es:[di],ax              ; 
                jmp     ExecCmdNext             ;look for more to do


                ALIGN   4
ExecOUTW:                                       ;================ PMICMD_OUTW
                test    [si].usFlags,PMIFLAG_REGISTER
                jnz     short @F                ; 
                mov     ax,[si].usData          ; 
                out     dx,ax                   ;write word DS:SI to port
                jmp     ExecCmdNext             ;look for more to do
@@:             mov     ax,es:[di]              ; 
                out     dx,ax                   ; 
                jmp     ExecCmdNext             ;look for more to do


                ALIGN   4
ExecBOUTB:                                      ;================ PMICMD_BOUTB
                mov     bx,[si].usDataPort      ;DX has index port
                mov     cx,[si].usNumRegs       ;no of regs to write to
                mov     ah,al                   ;AH has index
                push    cx                      ; 
                test    [si].usFlags,PMIFLAG_ATC
                jnz     short ExecBOUTBATC      ;Special case ATC registers
                EVEN
ExecBOUTBLoop1:
                cli                             ;make this un-interruptable
                cmp     bx,dx                   ;indexed register?
                je      short @F                ; 
                mov     al,ah                   ; 
                out     dx,al                   ;set the index
                inc     ah                      ;ready for next index
                xchg    bx,dx                   ;switch to data port
@@:             mov     al,es:[di]              ;get byte at es:[di]
                inc     di                      ; 
                inc     di                      ; 
                out     dx,al                   ; 
                xchg    bx,dx                   ;switch back to index port
                sti                             ; 
                dec     cx                      ; 
                jnz     ExecBOUTBLoop1          ; 
                pop     cx                      ; 
                jmp     ExecCmdNext             ;look for more to do

                ALIGN   4
ExecBOUTBATC:                                   ;special case ATC registers
                mov     bx,MISCOUTPUTREGREAD    ; 
                xchg    bx,dx                   ; 
                in      al,dx                   ;Read Misc Output register
                mov     dl,0bah                 ;assume mono
                test    al,1                    ; 
                jz      short @F                ; 
                add     dl,20h                  ;make it colour
@@:             mov     ax,[si].usStartReg      ;AX first index

ExecBOUTBLoop2:
                cli                             ;make this un-interruptable
                cmp     ah,10h                  ;check for mode control
                jnz     short NoVertSync1       ;not

                push    ax                      ;Start wait Vertical Sync
                push    cx                      ; 
                xor     cx,cx                   ;time out in case bit never set
@@:             in      al,dx
                test    al,8                    ;look for retrace bit
                loopnz  @B                      ;wait for retrace to end
                xor     cx,cx                   ;time out...
@@:             sti                             ;allow ints for a while
                nop                             ; 
                nop                             ;486     work around
                cli                             ;disable ints again
                in      al,dx                   ; 
                test    al,8                    ;look for retrace bit
                loopz   @B                      ;wait for retrace to begin again
                pop     cx                      ; 
                pop     ax                      ;End wait Vertical Sync....

NoVertSync1:    push    ax                      ;save index
                in      al,dx                   ;reset flip-flop
                xchg    bx,dx                   ;switch DX to ATC
                pop     ax                      ;recover index
                mov     al,ah                   ;get index
                out     dx,al                   ;set index value
                inc     ah                      ;bump up index
                mov     al,es:[di]              ;get byte at es:[di]
                inc     di                      ; 
                inc     di                      ; 
                out     dx,al                   ; 
                xchg    bx,dx                   ;switch DX to Status register
                sti                             ; 
                dec     cx                      ; 
                jnz     ExecBOUTBLoop2          ; 
                pop     cx
                jmp     ExecCmdNext             ;look for more to do


                ALIGN   4
ExecBINB:                                       ;================ PMICMD_BINB
                mov     bx,[si].usDataPort      ;DX has index port
                mov     cx,[si].usNumRegs       ;no of regs to write to
                push    cx                      ; 
                test    [si].usFlags,PMIFLAG_ATC
                jnz     short ExecBINBATC       ;Special case ATC registers
                EVEN
ExecBINBLoop1:
                cli                             ;make this un-interruptable
                cmp     bx,dx                   ; 
                je      short @F                ; 
                out     dx,al                   ;set the index
                inc     ax                      ;ready for next index
                xchg    bx,dx                   ;switch to data port
@@:             insb                            ;read byte to ES:DI,inc DI
                inc     di                      ;regs are words
                xchg    bx,dx                   ;switch back to index port
                sti                             ; 
                dec     cx                      ; 
                jnz     ExecBINBLoop1           ; 
                pop     cx                      ; 
                test    [si].usFlags,PMIFLAG_UPDATELIST
                jz      ExecCmdNext             ;look for more to do
                jmp     ExecListUpdate          ;update list with new values

                ALIGN   4
ExecBINBATC:                                    ;special case ATC registers
                mov     bx,MISCOUTPUTREGREAD    ; 
                xchg    bx,dx                   ; 
                in      al,dx                   ;Read Misc Output register
                mov     dl,0bah                 ;assume mono
                test    al,1                    ; 
                jz      short @F                ; 
                add     dl,20h                  ;make it colour
                ;V2.0URP01 start - changed [di] to [si]
@@:             mov     ax,[si].usStartReg      ;AX first index
                ;V2.0URP01 end

ExecBINBLoop2:
                cli                             ;make this un-interruptable
                cmp     al,10h                  ;check for mode control
                jnz     short NoVertSync2       ;not

                push    ax                      ;Start wait Vertical Sync
                push    cx                      ; 
                xor     cx,cx                   ;time out in case bit never set
@@:             in      al,dx
                test    al,8                    ;look for retrace bit
                loopnz  @B                      ;wait for retrace to end
                xor     cx,cx                   ;time out...
@@:             sti                             ;allow ints for a while
                nop                             ; 
                nop                             ;486     work around
                cli                             ;disable ints again
                in      al,dx                   ; 
                test    al,8                    ;look for retrace bit
                loopz   @B                      ;wait for retrace to begin again
                pop     cx                      ; 
                pop     ax                      ;End wait Vertical Sync....

NoVertSync2:    push    ax                      ;save index
                in      al,dx                   ;reset flip-flop
                xchg    bx,dx                   ;switch DX to ATC
                pop     ax                      ;recover index
                out     dx,al                   ;set index value
                inc     al                      ;bump up index
                inc     dx                      ; 
                insb                            ;read byte to ES:DI,inc DI
                inc     di                      ; 
                dec     dx                      ; 
                xchg    bx,dx                   ;switch DX to Status register
                sti                             ; 
                dec     cx                      ; 
                jnz     ExecBINBLoop2           ; 
                pop     cx
                ;V2.0URP01 start
                test    [si].usFlags,PMIFLAG_UPDATELIST
                jz      ExecCmdNext             ;look for more to do
                jmp     ExecListUpdate          ;update list with new values
                ;V2.0URP01 end


                ALIGN   4
ExecRMWB:                                       ;================ PMICMD_RMWB
                mov     bx,[si].usDataPort      ;DX has index port
                cli                             ;make this un-interruptable
                out     dx,al                   ;select index register
                mov     ah,al                   ;save index
                xchg    bx,dx                   ;switch to data port
                in      al,dx                   ;get data
                and     al,byte ptr [si].usANDMask      ;mask out some bits
                or      al,byte ptr [si].usORMask       ;set some bits
                xchg    al,ah                   ;index low,data high
                xchg    bx,dx                   ;switch to index port
                out     dx,al                   ;set index port again
                mov     al,ah                   ;get data
                mov     dx,bx                   ;set up data port
                out     dx,al                   ;rewrite the data
                sti                             ;allow ints again
                jmp     ExecCmdNext             ;look for more to do

                ALIGN   4
ExecRMWW:                                       ;================ PMICMD_RMWW
                cli                             ;make this un-interruptable
                in      al,dx                   ;get data
                and     ax,[si].usANDMask       ;mask out some bits
                or      ax,[si].usORMask        ;set some bits
                mov     dx,[si].usDataPort      ;write port
                out     dx,al                   ;write port again
                sti                             ;allow ints again
                jmp     ExecCmdNext             ;look for more to do

                ALIGN   4
ExecREGOP:                                      ;================ PMICMD_REGOP
                mov     bx,[si].usStartReg      ;BX first index
                shl     bx,1                    ; 
                lea     di,VarRegs[bx]          ;Get pointer to data
                mov     cx,[si].usData          ;has reg-op data
                mov     bx,[si].usIndexPort     ;has reg-op type
                shl     bx,1                    ; 
                jmp     cs:[bx + RegOpTable]    ;dispatch it

RegOpTable      dw      RegOpAssign             ; 
                dw      RegOpAND                ; 
                dw      RegOpOR                 ; 
                dw      RegOpXOR                ; 
                dw      RegOpSHL                ; 
                dw      RegOpSHR                ; 

                EVEN
RegOpAssign:    mov     es:[di],cx              ; 
                jmp     ExecCmdNext             ; 

                EVEN
RegOpAND:       and     es:[di],cx              ; 
                jmp     ExecCmdNext             ; 

                EVEN
RegOpOR:        or      es:[di],cx              ; 
                jmp     ExecCmdNext             ; 

                EVEN
RegOpXOR:       xor     es:[di],cx              ; 
                jmp     ExecCmdNext             ; 

                EVEN
RegOpSHL:       shl     word ptr es:[di],cl     ; 
                jmp     ExecCmdNext             ; 

                EVEN
RegOpSHR:       shr     word ptr es:[di],cl     ; 
                jmp     ExecCmdNext             ; 


;*
;* Execute a WAIT command by waiting for a 'status' register to set or
;* clear a bit. The wait will be performed a given number of times.
;* A timeout in milli seconds is implemented to force the wait to abort
;* if the given condition is not met. The GlobalInfoSeg milli-second
;* counter location is used but only has 32ms granularity.           
;*

                ALIGN   4
ExecWAIT:                                       ;================ PMICMD_WAIT
                push    es                      ; 
                mov     ax,DGROUP               ; 
                mov     es,ax                   ; 
                mov     es,es:_GlobalInfoSeg    ;ES:0 -> Global info seg
                mov     bx,[si].usStartReg      ;No of times to wait
                mov     cx,[si].usData          ;Timeout count
                mov     dx,[si].usIndexPort     ;Status port
                cmp     [si].usDataPort,PMI_WAIT_SET
                jz      short WaitSet           ; 
                cmp     [si].usDataPort,PMI_WAIT_CLEAR
                jz      short WaitClear         ; 
                pop     es                      ; 
                jmp     ExecCmdNext             ; 

                EVEN
WaitClear:      mov     edi,es:[4]              ; 
@@:             in      al,dx                   ; 
                test    al,byte ptr [si].usANDMask
                jz      short @F                ; 
                mov     eax,es:[4]              ; 
                sub     eax,edi                 ; 
                cmp     cx,ax                   ; 
                ja      @B                      ; 
@@:             dec     bx                      ; 
                jnz     WaitClear               ; 
                pop     es                      ; 
                jmp     ExecCmdNext             ; 

                EVEN
WaitSet:        mov     edi,es:[4]              ; 
@@:             in      al,dx                   ; 
                test    al,byte ptr [si].usANDMask
                jnz     short @F                ; 
                mov     eax,es:[4]              ; 
                sub     eax,edi                 ; 
                cmp     cx,ax                   ; 
                ja      @B                      ; 
@@:             dec     bx                      ; 
                jnz     WaitSet                 ; 
                pop     es                      ; 
                jmp     ExecCmdNext             ; 


;*
;* Jump here in order to scan back through the command list and update
;* register assignment entries with values from the local 'pseudo-registers'
;* VarRegs[]. This requires updating only those entries referenced by the
;* current command, typically a BINB() which is part of a save-state, not
;* a mode set or restore.
;*
;* Registers on entry:
;*      SI -> current list entry
;*      CX -> no of entries to update
;*
;* Procedure for update is to scan back through the list as follows:
;*
;*   do
;*    if entry is PMICMD_REGOP and                      (register operation)
;*                PMI_REGOP_ASSIGN and                  (register assignment)
;*                Referenced Register is in range
;*      update data
;*      decrement count of regs that need updating
;*   while (nodes exist) AND (count is non-zero)
;*
;* Note: If any register in range is assigned a value more than once prior
;*       to the BOUTB() (now a BINB()) command, the update will not be
;*       complete. The correct, safe method is to create a mask/list of
;*       dirty registers and mark them as updated when appropriate.
;*       Multiple assigments to the same register would be redundant, but
;*       would still cause the update to be incomplete.
;*

                ALIGN   4
ExecListUpdate:                                 ; 
                pusha                           ; 
                mov     ax,[si].usStartReg      ;AX first index
                mov     dx,cx                   ; 
                add     dx,ax                   ; 
                dec     dx                      ;Range is AX -> DX includive
                EVEN
ExecListTop:    mov     si,[si].pPrevCmd        ;get pointer to previous node
                or      si,si                   ;reached the end?
                jz      short ExecListDone      ;exit
                cmp     [si].usCommand,PMICMD_REGOP
                jnz     ExecListTop             ; 
                cmp     [si].usIndexPort,PMI_REGOP_ASSIGN
                jnz     ExecListTop             ; 
                mov     bx,[si].usStartReg      ;Index of referenced register
                cmp     bx,ax                   ; 
                jb      ExecListTop             ; 
                cmp     bx,dx                   ; 
                ja      ExecListTop             ; 
                shl     bx,1                    ;make word addressable
                mov     bx,es:VarRegs[bx]       ;get current 'new' value
                mov     [si].usData,bx          ;update list entry
                dec     cx                      ;mark another as done
                jnz     ExecListTop             ;more to do?

ExecListDone:   popa                            ; 
                jmp     ExecCmdNext             ;look for more to do

ExecCommands    ENDP


.286p

;/***************************************************************************
;*
;* FUNCTION NAME = _HardwareColor
;*
;* DESCRIPTION   = Determine if current mode is color or monochrome .
;*                 _HardwareColor is called by routines who need to know
;*                 whether the current mode is color or monochrome.
;*                 Determine whether color or monochrome mode to determine
;*                 which ports should be used.  This is done by checking
;*                 the I/O Address Select bit (b0) of the Miscellaneous
;*                 Output Register:  b0:  0 = Monochrome emulation mode, 1
;*                 = Color emulation mode
;*
;*                 ENTRY POINT: _HardwareColor
;*                   LINKAGE:   CALL FAR
;*
;* INPUT         = NONE
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = AX = 1 - color, 0 - monochrome
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

_HardwareColor  PROC    FAR

        mov     dx, MiscOutputRegRead   ; Read Miscellaneous Output Register
        in      al, dx
        and     ax, 1                   ; Return 1 - color, 0 - monochrome

        ret

_HardwareColor  ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = SETMAPMASK
;*
;* DESCRIPTION   = Address select a bit plane to read or write
;*                 SetMapMask is called by SaveRestorePVB in order to
;*                 address*/ a specific bit plane in graphics mode during
;*                 a 3xBox display buffer save or restore
;*
;*                 ENTRY POINT: SetMapMask
;*                 LINKAGE:   CALL FAR
;*
;* INPUT         = (Passed on stack)
;*                 WORD ReadMapMask  ( 0 - 3, 4 = write )
;*                 WORD WriteMapMask ( 1, 2, 4, or 8 )
;*
;* RETURN-NORMAL = Bit plane selected for read or write
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

SETMAPMASK  PROC    FAR

;/*
;** ENTRY: SS:SP = offset selector mask
;*/
        push    bp
        mov     bp, sp
        push    dx
        push    ax

        cli

        .if     < ReadMap eq 0 > or
        .if     < MapMask eq 1 >

;/*
;**  Set graphics mode register just for the first bit plane
;*/

            mov     dx, GraphAddressPort
            mov     ax, 0001h           ; Start from Set/Reset regs.    ;
            .repeat                     ;                               ;
                out     dx, al          ; Setup address port            ;
                inc     dx              ; Index to data port            ;
                inc     ax              ; Index to next address port    ;
                xchg    al,ah           ;                               ;
                out     dx,al           ; Clear Graphics register       ;
                dec     dx              ; Back to address port          ;
                xchg    al,ah           ;                               ;
                cmp     al,6            ;                               ;
            .until  <z>                 ;                               ;

            .if     < MapMask eq 1 >    ; 1st write request?            ;
                mov     al,8            ;                               ;
                out     dx,al           ; Index to Bit Mask register    ;
                inc     dx              ;                               ;
                mov     al,0ffh         ;                               ;
                out     dx,al           ; Allow write to all bits       ;
            .endif                      ;                               ;
        .endif                          ;                               ;

        .if     < ReadMap ne WriteFunction >

;/*
;** Reading from display buffer, address select specified bit plane
;*/

            mov     dx, SeqAddressPort
            mov     al, 02h             ; Memory map register in sequencer
            out     dx, al
            inc     dx
            mov     al, 0Fh
            out     dx, al              ; Memory map = 00001111


;/*
;** Address select specified bit plane to read
;*/

            mov     dx, GraphAddressPort
            mov     al, 04h             ; Read map select register
            out     dx, al
            inc     dx
            mov     al, ReadMap
            out     dx, al              ; Read map select  = 0, 1, 2, 3
        .else

;/*
;** Writing to display buffer, address select specified bit plane
;*/

            mov     dx, SeqAddressPort
            mov     al, 02h             ; Memory map register in sequencer
            out     dx, al
            inc     dx
            mov     al, MapMask
            out     dx, al              ; Memory map
        .endif

        sti

        pop     ax
        pop     dx
        pop     bp
        ret     4                       ; Remove 2 words from stack

SETMAPMASK  ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = ACCESSVIDEOENABLE
;*
;* DESCRIPTION   = Set or read physical video enable
;*                 AccessVideoEnable is called to query or set the physical
;*                 video enable setting.
;*
;*                 ENTRY POINT: AccessVideoEnable
;*                 LINKAGE:   CALL FAR
;*
;* INPUT         = (Passed on stack)
;*                 WORD  Direction ( GET or SET )
;*                 DWORD VideoEnable ( far pointer to word )
;*
;* RETURN-NORMAL = Video enable is set or returned
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

ACCESSVIDEOENABLE PROC FAR

        push    bp
        mov     bp, sp
        push    es
        push    di

        les     di, ParameterPacket     ; es:di = underscore scan line

        cli                             ; clear interrupts


        @SetAttAddressPort              ; Set the Attribute Address Port

        .if     < Direction eq GET >    ; 
            mov     dx,AttDataWritePort ; 
            in      al,dx               ; 
            mov     WORD PTR es:[di].VideoEnable, 0 ;Disabled
            test    al, 20h             ; 
            .if     < nz >              ; 
                inc     WORD PTR es:[di].VideoEnable ;Enabled
            .endif                      ; 
        .else                           ; 

            sub     ax,ax               ; Preset Disable video
            .if     < <WORD PTR es:[di].VideoEnable> ne ax >
                mov     al, 20h         ; Set Enable video
            .endif
            out     dx, al              ; Write to the port

        .endif                          ; 

        sti
        pop     di
        pop     es
        pop     bp
        ret     8

ACCESSVIDEOENABLE ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = ACCESSCLUT
;*
;* DESCRIPTION   = AccessCLUT for VGA
;*                 Set or read physical color lookup table
;*                 AccessCLUT is called to query or set the physical
;*                 color lookup table.
;*
;*                 ENTRY POINT: AccessCLUT
;*                 LINKAGE:   CALL FAR
;*
;* INPUT         = (Passed on stack)
;*                 WORD  Direction ( GET or SET )
;*                 DWORD  ColorLookupTable  ( far pointer to structure )
;*                          DWORD DataArea ( far pointer to table )
;*                          WORD  FirstEntry
;*                          WORD  NumEntries
;*
;* RETURN-NORMAL = Color lookup table is altered or queried
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

ACCESSCLUT      PROC    FAR

        push    bp
        mov     bp, sp
        push    es
        push    di

        les     di, ParameterPacket     ; es:di = palette packet

        mov     cx, WORD PTR es:[di].NumEntries ; # of triplets  ;
        mov     ax, WORD PTR es:[di].FirstEntry ; starting reg  ;
        les     di, DWORD PTR es:[di].DataArea

        .if     < Direction eq SET >    ; 
            mov     dx, PELAddressWrite ;03c8h
            cli                         ; 
            out     dx, al              ; 
            mov     dx, PELDataRegister ;03c9h Get data register ;
            .repeat                     ; 
                cli                     ; 
                mov     al, BYTE PTR es:[di] ; 
                out     dx, al          ; Set register value ;
                inc     di              ; 
                mov     al, BYTE PTR es:[di] ; 
                out     dx, al          ; Set register value ;
                inc     di              ; 
                mov     al, BYTE PTR es:[di] ; 
                out     dx, al          ; Set register value ;
                sti                     ; 
                inc     di              ; 
            .loop                       ; 
        .else                           ; 
            cld                         ; 
            mov     dx, PELAddressRead  ;03c7h
            cli                         ; 
            out     dx, al              ; 
            mov     dx, PELDataRegister ;03c9h Get data register ;
            mov     ah, 3Fh             ; Default 6-bit DAC

            .repeat                     ; 
                cli                     ; 
                in      al, dx          ; Set register value ;
                and     al, ah          ; Isolate color ( x bits ) ;
                stosb                   ; 
                in      al, dx          ; Set register value ;
                and     al, ah          ; Isolate color ( x bits ) ;
                stosb                   ; 
                in      al, dx          ; Set register value ;
                sti                     ; 
                and     al, ah          ; Isolate color ( x bits ) ;
                stosb                   ; 
            .loop                       ; 

        .endif

        pop     di
        pop     es
        pop     bp
        ret     6

ACCESSCLUT      ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = ACCESSHARDWARE
;*
;* DESCRIPTION   = Set or read physical indexed registers
;*                 AccessHardware is called to query or set the physical
;*                 indexed registers - Sequencers, Attributes, CRTs, or
;*                 Graphics
;*
;*                 ENTRY POINT: AccessHardware
;*                 LINKAGE:   CALL FAR
;*
;* INPUT         = (Passed on stack)
;*                 DWORD  RegAddress ( far pointer to structure )
;*                           WORD RegAddressPort
;*                           WORD RegDataPort
;*                           WORD RegColorAdjust
;*                           WORD RegFlags
;*                 WORD   DataType ( BYTES or WORDS )
;*                 WORD   Direction ( GET or SET )
;*                 DWORD  RegData ( far pointer to structure )
;*                           DWORD DataArea  ( far pointer to data table )
;*                           WORD  FirstEntry
;*                           WORD  NumEntries
;*
;* RETURN-NORMAL = Indexed registers are altered or queried
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

ACCESSHARDWARE PROC FAR

        push    bp
        mov     bp, sp
        push    es
        push    di
        push    ds
        push    si

        les     di, ParameterPacket     ; es:di = data packet
        lds     si, AddressPacket       ; ds:di = address packet

        mov     bx, WORD PTR  es:[di].FirstEntry ; Get starting reg
        mov     cx, WORD PTR  es:[di].NumEntries ; Get number of regs
        les     di, DWORD PTR es:[di].DataArea

        .if     < ds:[si].RegFlags eq Attributes_CMD >

;/*
;** To access the attribute address register, an 'in' must be issued to
;** the Attribute Controller at address 03BA for mono or 03DA for color.
;*/

            mov     dx, AttCtlInitializeReg ; Feature Control Register - init AttCtl flip-flop
            .if     < ColorMode eq TRUE >
                add     dx, ColorAdjustment
            .endif
        .endif

        push    cx                      ;@drw save this to check later
        push    dx                      ;@B19

HARDWARE_REG:
        cli                             ;B717771
        .if     < ds:[si].RegFlags eq Attributes_CMD >
            pop     dx                  ;@B19
            in      al, dx              ; Read port to reset flip-flop
            push    dx                  ;@B19
        .endif

        mov     dx, ds:[si].RegAddressPort ; Address Register
        .if     < ColorMode eq TRUE >
            add     dx, ds:[si].RegColorAdjust
        .endif
         ;           start
        .if     < ds:[si].RegFlags eq Sequencers_CMD > AND  ; 
        .if     < Direction eq SET >                        ; 


            push ax
            push dx
            mov     dx, SeqAddressPort  ; Sequencer address port
            mov     al, 0               ; Reset register index
            out     dx, al
            mov     dx, SeqDataPort     ; Sequencer data port
            mov     al, 0               ; Enable Sequencers again
            out     dx, al
            pop dx
            pop ax
        .endif
         ;           end
        mov     al, bl                  ; Address Palette Reg ( 00 - 0F )

        ; wait for vertical retrace to avoid glitching VRAM ;@B19
        .if     <al EQ 10h> AND         ;@B19
        .if     <dx EQ AttAddressPort>  ;@B19
            call    VGAWait             ;@B19
        .endif                          ;@B19

        out     dx, al                  ; Set up for get palette reg

        mov     dx, ds:[si].RegDataPort ; Address Register
        .if     < ColorMode eq TRUE >
            add     dx, ds:[si].RegColorAdjust
        .endif

        .if     < Direction eq SET >
            mov     al, BYTE PTR es:[di]
            out     dx, al
        .else

            in      al, dx
            .if     < WordByte eq WORDS >
                xor     ah, ah
                mov     WORD PTR es:[di], ax
            .else
                mov     BYTE PTR es:[di], al
            .endif

        .endif

        inc     bx
        .if     < WordByte eq WORDS >
            inc     di                  ; Point to next byte
        .endif
        inc     di                      ; Point to next byte

        sti                             ;B717771
        loop    HARDWARE_REG

        pop     dx                      ;@B19
        pop     cx                      ;@drw recover bytes processed

        cli                             ;B717771
                                        ;           test for single byte transfer removed
        .if     < ds:[si].RegFlags eq Sequencers_CMD > AND  ; 
        .if     < Direction eq SET >                        ; 


            mov     dx, SeqAddressPort  ; Sequencer address port
            mov     al, 0               ; Reset register index
            out     dx, al
            mov     dx, SeqDataPort     ; Sequencer data port
            mov     al, 3               ; Enable Sequencers again
            out     dx, al
        .endif

        sti

        pop     si
        pop     ds
        pop     di
        pop     es
        pop     bp
        ret     14

ACCESSHARDWARE ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = VGAWait
;*
;* DESCRIPTION   = wait for video retrace before a write to port
;*                 3C0h, register 10h
;*
;*                 ENTRY POINT: VGAWait
;*                 LINKAGE:   CALL FAR
;*
;* INPUT         = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

VGAWait PROC    NEAR
        sti
        push    ax
        push    cx
        push    dx
        mov     dx, FeatureControlWrite
        .if     < ColorMode eq TRUE >
            add     dx, 20h             ; use color ports
        .endif
        xor     cx,cx           ; time out in case a card never sets this bit
VGAWait1:
        in      al,dx
        test    al,8            ; look for retrace bit
        loopnz  VGAWait1        ; wait for retrace to end
        xor     cx,cx           ; time out in case a card never sets this bit
VGAWait2:
        sti
        nop
        nop                     ; 486     work around
        cli
        in      al,dx
        test    al,8            ; look for retrace bit
        loopz   VGAWait2        ;wait for retrace to begin again

        pop     dx
        pop     cx
        pop     ax
        ret
VGAWait ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = ACCESSREGISTER
;*
;* DESCRIPTION   = Set or read physical non-indexed register
;*                 AccessRegister is called to query or set the physical
;*                 non-indexed register specified
;*
;*                 ENTRY POINT: AccessRegister
;*                 LINKAGE:   CALL FAR
;*
;* INPUT         = (Passed on stack)
;*                 DWORD  RegAddress ( far pointer to structure )
;*                           WORD RegAddressPort
;*                           WORD RegDataPort
;*                           WORD RegColorAdjust
;*                           WORD RegFlags
;*                 WORD   Direction ( GET or SET or SETWORD )
;*                 DWORD  RegData ( far pointer to data )
;*
;* RETURN-NORMAL = Non-indexed register is altered or queried
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

ACCESSREGISTER PROC FAR

        push    bp
        mov     bp, sp
        push    es
        push    di
        push    ds
        push    si

        les     di, ParameterPacket     ; es:di = data packet
        lds     si, AddressPkt          ; ds:si = address packet

        mov     dx, ds:[si].RegDataPort ; Register port

        cli

        .if     < Direction eq SET >

            .if     < ds:[si].RegDataPort eq MiscOutputRegWrite >
                push    dx              ; Save register address
                mov     dx, SeqAddressPort ; Sequencer address port
                mov     al, 0           ; Reset register index
                out     dx, al
                mov     dx, SeqDataPort ; Sequencer data port
                inc     al              ; Synchronous reset
                out     dx, al

                pop     dx              ; Retrieve register address
                mov     al, BYTE PTR es:[di] ; Set miscoutput register

                out     dx, al


                mov     dx, SeqAddressPort ; Sequencer address port
                mov     al, 0           ; Reset register index
                out     dx, al
                mov     dx, SeqDataPort ; Sequencer data port
                mov     al, 3           ; Normal operation
                out     dx, al
            .else

                mov     al, BYTE PTR es:[di]
                out     dx, al

            .endif

        .else
            .if     < Direction eq SETWORD >

                mov     ax, word ptr es:[di]
                out     dx,ax

            .else                       ; < Direction eq GET >

                in      al, dx
                stosb

            .endif
        .endif

        sti

        pop     si
        pop     ds
        pop     di
        pop     es
        pop     bp
        ret     10

ACCESSREGISTER ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = CharFontBegin
;*
;* DESCRIPTION   = Address the font buffer
;*                 CharFontBegin is called by routines in order to address
;*                 the font buffer for writing or reading
;*
;*                 ENTRY POINT: CharFontBegin
;*                 LINKAGE:   CALL FAR
;*
;* INPUT         = NONE
;*
;* RETURN-NORMAL = Font buffer is address selected at A0000
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

CharFontBegin   PROC NEAR

                push    dx
                mov     dx, GraphAddressPort
                mov     ax, 0204h

                cli
                out     dx, ax                  ; Read Map Select register

                mov     ax, 0005h
                out     dx, ax                  ; Graphics Mode register

                mov     ax, 0406h
                out     dx, ax                  ; Graphics Misc register

                mov     dx, SeqAddressPort
                mov     ax, 0402h               ; write font to bp only bit plane 0
                out     dx, ax                  ; Write Map Select

                mov     ax, 0404h
                out     dx, ax                  ; Turn on odd/even

                sti

                pop     dx
                ret

CharFontBegin   ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = CharFontEnd
;*
;* DESCRIPTION   = Deselect the font buffer
;*                 CharFontEnd and  CharFontEnd2 are called following
;*                 completion of accessing the font buffer.
;*
;*                 ENTRY POINT: CharFontEnd,  CharFontEnd2
;*                 LINKAGE:   CALL FAR
;*
;* INPUT         = NONE
;*
;* RETURN-NORMAL = Font buffer address is deselected
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

CharFontEnd     PROC    NEAR

                push    ax

                mov     dx, GraphAddressPort
                mov     ax, 0004h

                cli
                out     dx, ax                  ; Read Map Select register

                mov     ax, 1005h
                out     dx, ax                  ; Graphics Mode register

                mov     ax, 0E06h               ; Setup colour buffer with chaining
                out     dx, ax                  ; Graphics Misc register

                mov     dx, SeqAddressPort
                mov     ax, 0302h
                out     dx, ax                  ; Write Map Select

                mov     ax, 0004h
                out     dx, ax                  ; Turn off odd/even

                sti
                pop     ax

                ret

CharFontEnd     ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = AccessFont
;*
;* DESCRIPTION   = Load the specified font into the font buffer
;*                 AccessFont is called by SetHWMode to load a font.
;*
;*                 ENTRY POINT: AccessFont
;*                 LINKAGE:   CALL FAR
;*
;* INPUT         = (Passed on stack)
;*                 DWORD  FontData ( far pointer to Font to load )
;*                 DWORD  VideoFontBuffer (far pointer to font buffer )
;*                 WORD   BoxHeight (vertical resolution/character rows )
;*                 WORD   BoxWidth  (horizontal resolution/character cols)
;*                 WORD   ModeColor (write to color/mono ports)
;*
;* RETURN-NORMAL = Specified video text font is loaded.
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

FontPtr         equ     DWORD PTR [bp+14]
VideoFontPtr    equ     DWORD PTR [bp+10]
BoxHeight       equ     WORD  PTR [bp+8]
BoxWidth        equ     WORD  PTR [bp+6]

ACCESSFONT      PROC    FAR

                push    bp
                mov     bp, sp
                push    ds
                push    es
                push    di
                push    si

                call    CharFontBegin   ; Map font buffer at A0000

                lds     si, FontPtr     ; ds:si = font to be loaded
                les     di, VideoFontPtr; es:di = Font buffer in PVB
                                        ; ds:si = font to be loaded
                mov     dx, di
                mov     cx, 1000h       ; clear out the 8k PVB font buffer
                xor     ax, ax
                rep     stosw
                mov     di, dx

                mov     ax, 256         ; For all 256 characters
                mov     dx, BoxHeight   ; 8, 14, or 16

                .repeat
                mov     cx, dx
                rep     movsb           ; Refresh one character of the font
                add     di, 32
                sub     di, dx
                dec     ax
                .until  z

                call    CharFontEnd     ; Done loading font

                pop     si
                pop     di
                pop     es
                pop     ds
                pop     bp

                ret     12

ACCESSFONT      ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = SETNUMBER9CLK                                           ;          
;*
;* DESCRIPTION   = Set the video clock on #9 adapter with S3
;*
;*                 ENTRY POINT: SetNumber9Clk
;*                 LINKAGE:   CALL FAR
;*
;* INPUT         = (Passed on stack)
;*                 horizontal resolution
;*
;* RETURN-NORMAL = Specified clock bits set serially.
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/
 CLOCK_DELAY0           EQU     73              ; delay loop for 1/8 sec
 CLOCK_DELAY1           EQU     145             ; delay loop for 1/4 sec

 C_NONE                 EQU     0       ; for freq synth programming
 C_CLK                  EQU     1       ; for freq synth programming
 C_DATA                 EQU     2       ; for freq synth programming
 C_BOTH                 EQU     3       ; for freq synth programming
 CLOCK_DELAY            EQU     CLOCK_DELAY1

 DELAY_CLOCK_SETUP      EQU     1       ; Delay when changing freq

N9HRes            equ     WORD  PTR [bp+6]

SETNUMBER9CLK  PROC    FAR

        push    bp
        mov     bp, sp

        push    di
        push    cx
        push    dx

        mov     cx,N9HRes               ;horizontal resolution in cx

;****************************************************************
; Set the frequency generator to the desired VGA frequency.
; Read the register CR42 to determine the frequency to run at.
; This register is used as an offset into the frequency table.
;****************************************************************
;Set_Nine_VGAFreq        PROC    NEAR
;       PUSH    BX                      ; save the mode number!!

        MOV     DX,03CCh                ; read clock bits in Misc Output reg
        IN      AL,DX
        TEST    AL,08h                  ; do we need to do this routine?
        JZ      SHORT No_Frequency_Setup; no, so just set m clock

        PUSH    SI                      ; save SI before i use it
        CALL    Find_6845_Address       ; Get CRT index address
        CALL    New_Refresh_Selected    ; put higher refresh in SI:BX
;;      JC      SHORT Hi_Refresh

;;      CALL    Freq_Setup              ; setup AX register for freq call
;;      MOV     AL, AH                  ; set to normal read
;;      AND     AX,0Fh                  ; AX is an index into freq table
;;      SHL     AL,1                    ; Convert to dword index
;;      SHL     AL,1                    ; Convert to dword index
;;      MOV     SI,OFFSET Frequency_Table ; Get offset for ICD2061A table
;;      ADD     SI,AX
;;      LODS    WORD PTR CS:[SI]        ; Read the low clock word
;;      MOV     BX,AX                   ; low word goes in BX
;;      LODS    WORD PTR CS:[SI]        ; Read the high clock word
;;      MOV     SI,AX                   ; SI,BX now has the 24-bit clock #

Hi_Refresh:
        MOV     AL, 42h                 ; go to the mode ctrl reg (CR42)
        out     dx, al
        inc     dx
        IN      AL,DX                   ; and read it again
        dec     dx
        AND     AL,0F0h
        MOV     AH,AL                   ; save this for later
        OR      AH,2                    ; set the index to two
        CALL    Set_Frequency           ; set the frequency synthesizer
        POP     SI                      ; restore the original contents

No_Frequency_Setup:
;       POP     BX                      ; restore the mode number

        pop     dx
        pop     cx
        pop     di

        pop     bp

        ret     2                       ;bump stack by 2Bytes

Frequency_Table:
;;      DD      0835178h        ; Index   0: Frequency =  25.172284
;;      DD      08AC158h        ; Index   1: Frequency =  28.325095
;;      DD      0A2E140h        ; Index   2: Frequency =  40.006679
;;      DD      0835178h        ; Index   3: Reserved
;;      DD      083587Ah        ; Index   4: Frequency =  49.999992
;;      DD      0A3B056h        ; Index   5: Frequency =  76.999990
;;      DD      09AA942h        ; Index   6: Frequency =  35.999995
;;      DD      0AB8946h        ; Index   7: Frequency =  44.889429
;;      DD      0835178h        ; Index   8: Reserved
;;      DD      0835178h        ; Index   9: Reserved
;;      DD      0A2E040h        ; Index  10: Frequency =  80.013358
;;      DD      093B16Ah        ; Index  11: Frequency =  31.499996
;;      DD      0B22822h        ; Index  12: Frequency = 108.516732
;;      DD      0968846h        ; Index  13: Frequency =  65.012276
;;      DD      09F5850h        ; Index  14: Frequency =  74.999990
;;      DD      0835178h        ; Index  15: Reserved
;Set_Nine_VGAFreq        ENDP

SETNUMBER9CLK   ENDP

;****************************************************************
;
;
;****************************************************************
; Set the frequency synthesizer to the desired frequency.
; The frequency synthesizer is an IDC 2061a part.
; This code turns off the video display prior to setting
; the synthesizer, and then returns the display to the
; previous state.
;       Entry:  SI:BX   - pointer to 24 bit clock Number
;               AH      - contains the saved CR42 reg
;               dx      - 3?4
;****************************************************************
Set_Frequency   PROC    NEAR
        PUSH    DX
;;      PUSH    AX                      ; save CR42

        PUSH    AX                      ; save CR42 (twice!)
;;;     CALL    Find_6845_Address       ; Get CRT index address
        MOV     AX, 0A039H              ; register lock 1 index
        OUT     DX, AX                  ; Unlock the soft switches
        POP     AX                      ; restore the saved CR42 in AX

        MOV     AL, 1                   ; To turn off video display
        MOV     DX, 03C4h               ; go to the Clocking Mode register
        OUT     DX, AL
        INC     DX
        IN      AL, DX                  ; read the Clocking Mode reg
        PUSH    AX                      ; save the Clocking Mode Reg
        OR      AL, 20h                 ; set video OFF bit
        OUT     DX, AL                  ; turn off video output

        MOV     DX, 03CCh               ; read clock bits in Misc Output reg
        IN      AL, DX
        PUSH    AX                      ; save this old value
        OR      AL, 0Ch                 ; enable CR42 bits 2 & 3
        MOV     DX, 03C2h
        OUT     DX, AL                  ; write new misc output value

        CALL    Find_6845_Address       ; Get CRT index address
        MOV     AL, 42h                 ; the Mode Control Reg (CR42)
        OUT     DX, AL                  ; write index to 3?4
        INC     DX                      ; Move DX to 3?5
        XCHG    AH,AL                   ; Swap Index/Data
        OUT     DX,AL                   ; Program the data

;**************************************************************************
;***************** START OF CRITICAL SECTION OF CODE **********************
;**************************************************************************
        PUSHF                   ; save the flags prior to critical section
        CLI                     ; no interupts please...

        MOV     AL, C_NONE      ; start programming ICD2061A
        OUT     DX, AL
        MOV     AL, C_DATA
        OUT     DX, AL
        MOV     CX, 6

unLockSeq:
        MOV     AL, C_BOTH       ; Unlock sequence
        OUT     DX, AL
        MOV     AL, C_DATA
        OUT     DX, AL
        LOOP    SHORT unLockSeq

        MOV     AL, C_NONE
        OUT     DX, AL
        MOV     AL, C_CLK
        OUT     DX, AL
        MOV     AL, C_NONE
        OUT     DX, AL
        MOV     AL, C_CLK
        OUT     DX, AL

        SAR     SI, 1            ; do shift (ignore 1st bit)
        RCR     BX, 1            ; set the carry flag
        MOV     CX, 24           ; Program the 24 bit value into REG0

programREG0:
        SAR     SI, 1            ; clock_value >>= 1
        RCR     BX, 1
        JNC     SHORT clock0

clock1:
        MOV     AL, C_CLK
        OUT     DX, AL
        MOV     AL, C_NONE
        OUT     DX, AL
        MOV     AL, C_DATA
        OUT     DX, AL
        MOV     AL, C_BOTH
        OUT     DX, AL
        JMP     SHORT programCont
clock0:
        MOV     AL, C_BOTH
        OUT     DX, AL
        MOV     AL, C_DATA
        OUT     DX, AL
        MOV     AL, C_NONE
        OUT     DX, AL
        MOV     AL, C_CLK
        OUT     DX, AL

programCont:
        LOOP    SHORT programREG0

        MOV     AL, C_BOTH
        OUT     DX, AL
        MOV     AL, C_DATA
        OUT     DX, AL
        MOV     AL, C_BOTH
        OUT     DX, AL

        POPF                    ; resore all flags
        STI                     ; ok for interupts now
;**************************************************************************
;******************* END OF CRITICAL SECTION OF CODE **********************
;**************************************************************************

        MOV     AL, AH          ; AH has contained the saved CR42 reg
        OUT     DX, AL          ; Select the VCLOCK index in CR42

;;IF      DELAY_CLOCK_SETUP
;;      MOV     CX,CLOCK_DELAY  ; Brief wait period (defined above)
;;      MOV     AL, 0           ; no tone during wait period
;;;     CALL    Gate_Timer      ; Wait for Clock Freq to synchronize
;;ENDIF

        POP     AX              ; old Misc Output reg value
        MOV     DX, 03C2h       ; write to write port
        OUT     DX, AL          ; reset the value

        MOV     DX, 03C4h       ; To set screen to previous state
        MOV     AL, 1           ; go to the Clocking Mode reg
        OUT     DX, AL
        INC     DX
        POP     AX              ; use old value pushed from Clk Mode Reg
        OUT     DX, AL          ; restore video

;;      POP     AX              ; restore the saved CR42 in AX
        POP     DX              ; Restore callers registers

        RET
Set_Frequency           ENDP
;****************************************************************
;
;
;****************************************************************
; If the scratch register calls for it, set the new refresh rate
; for the vesa modes (CR52 contains selected refresh rate)
; CR52 is initialized to 0AAh at init time.
;
; The structure for CR52 is:
;   Bits:  7 6| 5 4| 3 2| 1 0
;          x x| x x| x x| x x
;                       
;           ³    ³    ³    ÀÄ 640x480 resolutions
;           ³    ³    ÀÄÄÄÄÄÄ 800x600 resolutions
;           ³    ÀÄÄÄÄÄÄÄÄÄÄÄ 1024x768 resolutions
;           ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ- 1280x1024 and 1600x1200 resolutions
;
; where:    00 = 70 Hz
;           01 = 76 Hz
;           10 = 60/56 Hz (this is really Default Hz!)
;           11 = 72 Hz
;
;    cx = horizontal resolution
;    dx = 3?4
;****************************************************************
New_Refresh_Selected    PROC    NEAR    ; Test GXe system subfunctions
;;      PUSH    AX
;;      PUSH    DI                      ; Dont destroy DI

        CMP     cx, 640                 ; which gross resolution?
        JE      SHORT Refresh_640
        CMP     cx, 800                 ; which gross resolution?
        JE      SHORT Refresh_800
        CMP     cx, 1024                ; which gross resolution?
        JE      SHORT Refresh_1024
        CMP     cx, 1280                ; which gross resolution?
        JE      SHORT Refresh_1280
        CMP     cx, 1600                ; which gross resolution?
        JE      SHORT Refresh_1600
        CMP     cx, 1056                ; 132 cols text
        JE      SHORT Refresh_132
        CMP     cx, 1188                ; 132 cols text
        JE      SHORT Refresh_132
        JMP     SHORT No_HiRefresh      ; fail-safe exit

Refresh_640:
        MOV     CL, 0                   ; offset in CR52 is 1'st position
        MOV     BL, 0                   ; offset in table is...
        JMP     SHORT Set_Refresh

Refresh_800:
        MOV     CL, 2                   ; offset in CR52 is 2'nd position
        MOV     BL, 4                   ; offset in table is...
        JMP     SHORT Set_Refresh

Refresh_1024:
        MOV     CL, 4                   ; offset in CR52 is 3'rd position
        MOV     BL, 8                   ; offset in table is...
        JMP     SHORT Set_Refresh

Refresh_1280:
        MOV     CL, 6                   ; offset in CR52 is 4'th position
        MOV     BL, 12                  ; offset in table is...
        JMP     SHORT Set_Refresh

Refresh_1600:
        MOV     CL, 6                   ; offset in CR52 is 4'th position
        MOV     BL, 16                  ; offset in table is...

Set_Refresh:
        MOV     AL, 52h
        OUT     DX, AL                  ; CR52 is the multiple mon-id reg.
        INC     dx
        in      al, dx
        dec     dx                      ; Get CRT index address

;;;     XCHG    AL, AH                  ; Get Monitor info in AL
        SHR     AL, CL                  ; Get the corresponding monitor-id
        AND     AL, 03                  ; Get the bits I want by themselves
        MOV     BH, AL                  ; store bits here...

        CMP     BH, 02                  ; is register set to 60 Hz?
        JE      SHORT No_New_HSync      ; user wants 60 Hz

        MOV     AL, 04h                 ; read the Horiz Sync Start Pos
        OUT     DX, AL                  ; Unlock the soft switches
        INC     dx
        in      al, dx
        DEC     AL                      ; by one character clock
        OUT     DX, AL                  ; to adjust screen position.
        dec     dx                      ; Get CRT index address
        jmp     SHORT No_New_HSync      ; continue

Refresh_132:
        MOV     bh, 0                   ; initialize to 0
        MOV     BL, 20                  ; offset in table is...

No_New_HSync:
        MOV     AL, BH                  ; get the value back now
        XOR     AH, AH                  ; clear the AH reg for add to SI
        ADD     AL, BL                  ; add res jump for total index jump
        SHL     AL, 1                   ; convert to DWORD offset
        SHL     AL, 1                   ; convert to DWORD offset
        MOV     SI,OFFSET Hi_Refresh_Table ; Get offset for Hi_Refresh table
        ADD     SI, AX                  ; increment to the refresh rate
        LODS    WORD PTR CS:[SI]        ; Read the low clock word
        MOV     BX, AX                  ; low word goes in BX
        LODS    WORD PTR CS:[SI]        ; Read the high clock word
        MOV     SI, AX                  ; SI,BX now has the 24-bit clock #
        STC                             ; indicate the new numbers in regs
        JMP     SHORT End_Refresh       ; skip to the end with carry set

No_HiRefresh:
        CLC                             ; use standard frequency

End_Refresh:
;;      POP     DI
;;      POP     AX

        RET

;****************************************************************
Hi_Refresh_Table:
        DD      08FA170h        ; Index   0:  640 @ 70Hz  29.376 Mhz
        DD      093B16Ah        ; Index   1:  640 @ 76Hz  31.499 Mhz
        DD      08AC158h        ; Index   2:  640 @ 60Hz  28.325 Mhz
        DD      08F896Ah        ; Index   3:  640 @ 72Hz  30.198 Mhz
        DD      0AB5942h        ; Index   4:  800 @ 70Hz  44.999 Mhz
        DD      0AF8940h        ; Index   5:  800 @ 76Hz  48.850 Mhz
        DD      09AA942h        ; Index   6:  800 @ 56Hz  35.999 Mhz
        DD      0AEF138h        ; Index   7:  800 @ 72Hz  46.295 Mhz
        DD      0A2F846h        ; Index   8: 1024 @ 70Hz  75.847 Mhz
        DD      0A7804Ch        ; Index   9: 1024 @ 76Hz  82.329 Mhz
        DD      0968846h        ; Index  10: 1024 @ 60Hz  65.012 Mhz
        DD      0A26036h        ; Index  11: 1024 @ 72Hz  78.009 Mhz
        DD      093D86Eh        ; Index  12: 1280 @ 70Hz  126.5 / 2 Mhz
        DD      09B785Ah        ; Index  13: 1280 @ 76Hz  139.0 / 2 Mhz
        DD      08B486Eh        ; Index  14: 1280 @ 60Hz  108.5 / 2 Mhz
        DD      0937862h        ; Index  15: 1280 @ 72Hz  128.0 / 2 Mhz
        DD      0AAE03Ch        ; Index  16: 1600 @ 60Hz  170.028 Mhz
        DD      0AAE03Ch        ; Index  17: 1600 @ 60Hz  170.028 Mhz
        DD      0AAE03Ch        ; Index  18: 1600 @ 60Hz  170.028 Mhz
        DD      0AAE03Ch        ; Index  19: 1600 @ 60Hz  170.028 Mhz
        DD      0A2E140h        ; Index  20: 132 columns text mode
;****************************************************************
New_Refresh_Selected            ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = SETDIAMONDCLK
;*
;* DESCRIPTION   = Set the video clock on diamond speedstar 24X on per
;*                 mode basis.
;*
;*                 ENTRY POINT: SetDiamondClk
;*                 LINKAGE:   CALL FAR
;*
;* INPUT         = (Passed on stack)
;*                 horizontal resolution
;*                 text/graphics mode
;*                 #colors
;*
;* RETURN-NORMAL = Specified clock bits set serially.
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

HRes            equ     WORD  PTR [bp+10]         ;          
fbType          equ     WORD  PTR [bp+8]
nColor          equ     WORD  PTR [bp+6]

SETDIAMONDCLK  PROC    FAR

        jmp     program_clk_chip

        ;                                                  Monitor type
        ;NEC 5Fg ---------------------------------------------- 8x
        ;NEC 4Fg ---------------------------------------------- ax
        ;NEC 3Fgx --------------------------------------------- 9x
        ;NEC 3Fg, SONY 1302 ----------------------------------- 7x
        ;Fixed Freq, 8514 ------------------------------------- fx
        ; 
clk_data:
; BIOS verions 1.xx (>=1.00 and <2.00)
; supports ICD 2061 clock chip
; 24-bit serial data for clock frequency
table_2061      label   byte
 ;       bits 8-15, bits 0-7,  bits 15-23                |fx |7x |9x |ax |8x
 ;                                                       |---|---|---|---|---
 db      00010101b,00111101b,10100010b   ;0  - 25.175    |   |   |   |   |
 db      00000110b,00110101b,10010010b   ;1  - 28.322    |   |   |   |   |
 db      00011011b,10101101b,11010010b   ;2  - 31.5      |   |480|480|480|480
 db      00101010b,10000101b,11110010b   ;3  - 36.0      |600|   |   |   |
 db      00101010b,10111100b,11000010b   ;4  - 40.0      |   |   |   |   |
 db      00010000b,01010100b,11000010b   ;5  - 44.9      |768|768|   |   |
 db      00110101b,10111100b,10100010b   ;6  - 50.0      |   |   |600|600|600
 db      00011011b,10101100b,11010010b   ;7  - 63.0      |   |   |   |   |
 db      00100010b,11000100b,10110010b   ;8  - 65.0      |   |   |768|   |
 db      00101010b,10000100b,11110010b   ;9  - 72.0      |   |   |   |   |
 db      00110101b,00010100b,11110010b   ;10 - 75.00     |   |   |   |768|
 db      00100011b,10010100b,11110010b   ;11 - 77.25     |   |   |   |   |768
 db      00001101b,00010100b,11110010b   ;12 - 75.68     |   |   |   |   |
 db      00010101b,00111100b,10100010b   ;13 - 50.35     |   |   |   |   |

 db      00101010b,10111100b,10100010b   ;14 - xx.xx     |   |600|   |   |
 db      00011110b,10101100b,10100110b   ;15 - xx.xx     |480|   |   |   |

clk_data1:

; BIOS verions 2.xx (>=2.00)
; supports ICD 2061A clock chip
; 24-bit serial data for clock frequency
table_2061A     label   byte
 ;      ;bits 8-15, bits 0-7,  bits 15-23                |fx |7x |9x |ax |8x
 ;      ;                                                |---|---|---|---|---
 db      00010101b,00111101b,10000000b   ;0  - 25.175    |   |   |   |   |
 db      00000110b,00110101b,10100001b   ;1  - 28.322    |   |   |   |   |
 db      00011011b,10101101b,10010010b   ;2  - 31.5      |   |480|480|480|480
 db      00101010b,10000101b,10110010b   ;3  - 36.0      |600|   |   |   |
 db      00001110b,00000101b,10001010b   ;4  - 40.0      |   |   |   |   |
 db      00010000b,00101001b,10101010b   ;5  - 44.9      |768|768|   |   |
 db      00110101b,10111100b,10000010b   ;6  - 50.0      |   |   |600|600|600
 db      00011011b,10101100b,10010010b   ;7  - 63.0      |   |   |   |   |
 db      00100010b,11000100b,11010010b   ;8  - 65.0      |   |   |768|   |
 db      00101010b,10000100b,10110010b   ;9  - 72.0      |   |   |   |   |
 db      00110101b,00010100b,11110010b   ;10 - 75.00     |   |   |   |768|
 db      00100011b,10010100b,10001010b   ;11 - 77.25     |   |   |   |   |768
 db      00001101b,00010100b,10001010b   ;12 - 75.68     |   |   |   |   |
 db      00010101b,00111100b,10000010b   ;13 - 50.35     |   |   |   |   |

 db      00111101b,00000101b,11101110b   ;14 - xx.xx     |   |600|   |   |
 db      00011110b,10101100b,10000110b   ;15 - xx.xx     |480|   |   |   |
 db      00011011b,10111100b,10100110b   ;16 - xx.xx

color_256:
 db      00001001b,11110100b,11100110b   ;2nd pass for 256 color graphics modes

clk_data2:

; BIOS verions 5.xx (>=5.00 and <6.00)
; supports ICD 2061A clock chip
; 24-bit serial data for clock frequency
;table_2061A     label   byte
 ;      ;bits 8-15, bits 0-7,  bits 15-23                |fx |7x |9x |ax |8x
 ;      ;                                                |---|---|---|---|---
 db      00010101b,00111101b,10100010b   ;0  - 25.175    |   |   |   |   |
 db      00000110b,00110101b,10010010b   ;1  - 28.322    |   |   |   |   |
 db      00110101b,00010100b,11110010b   ;2  - 75.00     |   |   |   |768|
 db      00101010b,10000100b,11110010b   ;3  - 72.0      |   |   |   |   |
 db      00001110b,00000100b,11110010b   ;4  - 80.0      |   |   |   |   |
 db      00010000b,01010100b,11000010b   ;5  - 44.9      |   |   |   |   |
 db      00110101b,10111100b,10100010b   ;6  - 50.0      |   |   |   |600|
 db      00100010b,11000100b,10110010b   ;7  - 65.0      |   |   |   |480|
 db      00100111b,00110100b,11110010b   ;8  - 77.16     |   |   |   |   |

 db      00001110b,00000100b,10001010b   ;9  - xx.xx     |   |   |   |132|

clk_data3:

; BIOS verions 6.xx (>=6.00)
; supports ICD 2061A clock chip
; 24-bit serial data for clock frequency
;table_2061A     label   byte
 ;      ;bits 8-15, bits 0-7,  bits 15-23                |fx |7x |9x |ax |8x
 ;      ;                                                |---|---|---|---|---
 db      00010101b,00111101b,10000010b   ;0  - 25.175    |   |   |   |   |
 db      00000110b,00110101b,10100010b   ;1  - 28.322    |   |   |   |   |
 db      00110101b,00010100b,11110010b   ;2  - 75.00     |   |   |   |768|
 db      00101010b,10000100b,10110010b   ;3  - 72.0      |   |   |   |   |
 db      00001110b,00000100b,10001010b   ;4  - 80.0      |   |   |   |   |
 db      00010000b,00101001b,10101010b   ;5  - 44.9      |   |   |   |   |
 db      00110101b,10111100b,10000010b   ;6  - 50.0      |   |   |   |600|
 db      00100010b,11000100b,11010010b   ;7  - 65.0      |   |   |   |480|
 db      00011110b,01000100b,10001010b   ;8  - 77.16     |   |   |   |   |

 db      00001110b,00000100b,10001010b   ;9  - xx.xx     |   |   |   |132|

clk_data4:                        ;          
; BIOS version 3.33 (S3)
; supports ICD 2061A clock chip
; 24-bit serial data for clock frequency
;table_2061A     label   byte
 ;       bits 8-15  bits 0-7   bits 16-23                |fx |7x |9x |ax |8x
 ;                                                       |---|---|---|---|---
 db      00010101b,00111101b,10000010b   ;0  - 25.175    |   |   |   |   |
 db      00000110b,00110101b,10100010b   ;1  - 28.322    |   |   |   |   |
 db      00001110b,00000101b,10001010b   ;2  - 40.0      |   |480|480|480|480
 db      00100010b,11000101b,11010010b   ;3  - 32.5      |600|   |   |   |
 db      00110101b,10111100b,10000010b   ;4  - 50.0      |   |   |   |   |
 db      00100010b,11000100b,11010010b   ;5  - 65.0      |768|768|   |   |
 db      00101010b,10000101b,10110010b   ;6  - 36.0      |   |   |600|600|600
 db      00010000b,00101001b,10101010b   ;7  - 44.9      |   |   |   |   |
 db      00111111b,00001100b,11110010b   ;8  - 74.5      |   |   |768|   |
 db      00110111b,11010100b,10001010b   ;9  - 80.16     |   |   |   |   |
 db      00010101b,00111100b,10000010b   ;10 - 50.35     |   |   |   |768|
 db      00011011b,10101101b,10010010b   ;11 - 31.5      |   |   |   |   |768
 db      00011011b,10101100b,10010010b   ;12 - 63.0      |   |   |   |   |
 db      00011110b,01000100b,10001010b   ;13 - 77.16     |   |   |   |   |

;                                                        |   |600|   |   |
;                                                        |480|   |   |   |

clk_data5:
; BIOS versions 2.xx (S3)
; supports ICD 2061 clock chip
; 24-bit serial data for clock frequency
;table_2061      label   byte
 ;       bits 8-15  bits 0-7   bits 16-23                | 0 | 1 | 2 | 3 | 4
 ;                                                       |---|---|---|---|---
 db      00010101b, 00111101b, 10100010b ;0  - 25.175    |   |   |   |   |
 db      00000110b, 00110101b, 10010010b ;1  - 28.322    |   |   |   |   |
 db      00101010b, 10111100b, 11000010b ;2  - 40        |   |   |   |   |
 db      00100010b, 11000101b, 10110010b ;3  - 32.5      |   |   |   |   |
 db      00110101b, 10111100b, 10100010b ;4  - 50        |   |   |   |   |
 db      00100010b, 11000100b, 10110010b ;5  - 65.0      |   |   |   |   |
 db      00101010b, 10000101b, 11110010b ;6  - 36.0      |   |   |   |   |
 db      00010000b, 01010100b, 11000010b ;7  - 44.9      |   |   |   |   |
 db      00111111b, 00001100b, 11110010b ;8  - 74.5      |   |   |   |768|
 db      00110111b, 11010100b, 11110010b ;9  - 80.16     |   |   |   |   |
 db      00010101b, 00111100b, 10100010b ;10 - 50.35     |   |   |   |600|
 db      00011011b, 10101101b, 11010010b ;11 - 31.5      |   |   |   |480|
 db      00011011b, 10101100b, 11010010b ;12 - 63.0      |   |   |   |   |
 db      00100111b, 00110100b, 11110010b ;13 - 77.16     |   |   |   |   |

;                                                        |   |600|   |   |
;                                                        |480|   |   |   |

; for S3
;  input  si    points to 24-bit data for programmable clock
;  assumes video is disabled
;            Note on Diamond S3 implementations:
; Stealth VRAM uses the same serial 24bit clock programming as
; WD and TSENG 24 implementations. It is manufactured only with 924 chip
; and its monitor bits are configured by the configuration switches whitch
; are reflected thru the CRT 37 configuration register. The 924 chip is
; not supported, so the VRAM code in here is dormant. No code path leads to
; it. Both Stealth 24 and PRO use EEPROM data to get the 3 clock bytes, regardless
; of the BIOS revision. Therefore, SetDiamondClk supports 24 and PRO
; implementation only.

program_clk_chip:

        push    bp
        mov     bp, sp

        push    si
        push    di
        push    cx
        push    dx

        mov     cx,WORD PTR ds:_SVGAHardware            ;          
        cmp     cx,S3_ADAPTER
        jne     get_mon
        mov     cx, nColor              ;setup colors
        mov     dx, HRes                ;horizontal resolution in dx
        call    s3_proc                 ; support for Stealth 24 and PRO
        jmp     s3_done                 ;adapter is S3           

get_mon:
        call    GetMonitorType
        mov     al,byte ptr cs:[di]     ;mode index pointer
check_monitor:
        cmp     al, 254
        jne     supported_mode
        jmp     not_hires               ;unknown monitor type

supported_mode:

        mov     dx,HRes                 ;horizontal resolution in dx
        mov     cx,fbType               ;text or graphics?
        test    cx,2
        je      text_mode

        cmp     dx, 1024                ;1024x768x256/16 color modes
        jne     hres_800
        mov     dx,2
        jmp     short set_index
hres_800:
        cmp     dx, 800                 ;800x600x256/16 color modes
        jne     hres_640
        mov     dx,1
        jmp     short set_index
hres_640:
        cmp     dx, 640                 ;640x480x256/16 color modes
        je      mode_480
        jmp     not_hires
mode_480:
        mov     dx,0
        jmp     short set_index

text_mode:
        cmp     dx, 1188                ;132 column mode (24x)
        je      mode_132
        cmp     dx, 1056                ;132 column mode (24)
        jne     hres_720t
mode_132:
        mov     dx, 3
        jmp     short set_index
hres_720t:
        cmp     dx, 720                 ;80 column mode
        jne     hres_360t
        mov     dx, 4
        jmp     short set_index
hres_360t:
        cmp     dx, 360                 ;40 column mode
        je      set_360t                ;          
        jmp     short not_hires
set_360t:
        mov     dx, 5

set_index:
        add     di,dx
        xor     ah,ah
        mov     al,byte ptr cs:[di]     ;mode index pointer

        cmp     al, 255                 ;          
        jne     @F                      ;          
        jmp     short not_hires         ;                       ;          
@@:                                     ;          
        mov     cx,3                    ;width of data table
        mul     cx                      ;ax is now table aligned

        mov     cx,WORD PTR ds:_SVGAHardware
        cmp     cx,TSENG_ADAPTER
        je      ss24                    ;must be Western Digital

        mov     cx,WORD PTR ds:_OEMHardware+6   ;          
        cmp     cx,1                    ;BIOS Rev 1 (Major)
        jne     clk1
        lea     si, clk_data
        jmp     short supported
clk1:
        cmp     cx,2                    ;BIOS Rev 2 (Major)
        je      @F                      ;          
        jmp     short not_hires         ;bios rev not currently supported
@@:
        lea     si, clk_data1
        jmp     short supported

ss24:
        mov     cx,WORD PTR ds:_OEMHardware+6   ;          
        cmp     cx,5                    ;BIOS Rev 5 (Major)
        jg      ss24_clk2
        lea     si, clk_data2
        jmp     short supported

ss24_clk2:
        lea     si, clk_data3           ;BIOS Rev 6 (Major)
        jmp     short supported         ;          
;S3_VRAM:
;       lea     si, clk_data5           ;BIOS Rev 2 (Major)
;       add     si,ax                   ;point to serial data
;       call    program_clk_chip_s3

supported:
        add     si,ax                   ;point to serial data

        mov     dx,3cch
        in      al, dx
        push    ax

        mov     cx, 256
ploop:
        push    cx
        call    prog_clock

        mov     cx,WORD PTR ds:_OEMHardware+6   ;          
        cmp     cx,2                    ;BIOS Rev 2 (Major)
        jne     short done_prog

        lea     si, color_256   ;2nd pass for 256 color graphics modes
        call    prog_clock
done_prog:
        pop     cx
        in      al,dx
        test    al, 10h
        jnz     ppp_done
        sub     si,2
        loop    ploop

ppp_done:
        pop     ax
        out     dx,al

        call    VGAWait                 ;wait for vertical retrace

not_hires:
s3_done:                   ;          
        sti

        pop     dx
        pop     cx
        pop     di
        pop     si

        pop     bp

        ret     6                       ;bump stack by 2Bytes                       ;          

SETDIAMONDCLK   ENDP
        ; assumes dx = Hres
s3_proc:                                ;                     ;          

;       mov     cx, nColor              ;get #colors                               ;          
        mov     ax,eeprom_tab_l*index_m1024
        cmp     dx, 1024
        jne     short res_800

        cmp     cx, 16                  ;check for hi-color               
        jne     @F
        mov     ax,eeprom_tab_l*index_m1024h

@@:
        jmp     short   update_eeprom

res_800:
        cmp     dx, 800
        jne     short res_640
        cmp     cx, 16                  ;check for hi-color               
        jne     @F
        mov     ax,eeprom_tab_l*index_m800h
        jmp     short   update_eeprom

@@:
        cmp     cx, 24                  ;check for true-color             
        jne     @F
        mov     ax,eeprom_tab_l*index_m800t                    ;          
        jmp     short   update_eeprom

@@:
        mov     ax,eeprom_tab_l*index_m800
        jmp     short   update_eeprom

res_640:
        cmp     dx, 640
        jne     res_1280                ;check for 1280 res               
        cmp     cx, 16                  ;check for hi-color               
        jne     @F
        mov     ax,eeprom_tab_l*index_m640h
        jmp     short   update_eeprom

@@:
        cmp     cx, 24                  ;check for true-color             
        jne     @F
        mov     ax,eeprom_tab_l*index_m640t                    ;          
        jmp     short   update_eeprom

@@:
        mov     ax,eeprom_tab_l*index_m640
        jmp     short   update_eeprom

res_1280:                               ;          
        cmp     dx, 1280
        jne     @F
;;;;;;; Index_m1280_256 (10 = computed value of 64h) yields a flickering or   ;          
;;;;;;; strobe effect on the OS/2 and WinOS/2 desktop after a save/restore    ;          
;;;;;;; on 1280x1024x256.  Index_m1280_16 (9 = computed value of 5Ah) works   ;          
;;;;;;; for all refresh rates supported by the StlMode Utility (43.5, 60, 72) ;          
;;      mov     ax,eeprom_tab_l*index_m1280_256                               ;          
        mov     ax,eeprom_tab_l*index_m1280_16                                ;          
        jmp     short   update_eeprom

@@:
        cmp     dx, 1056
        jne     @F
        mov     ax,eeprom_tab_l*index_m5455
        jmp     short   update_eeprom
@@:

update_eeprom:
        call    upd_crtc_eeprom         ;stealth 24, PRO
        ret

;/***************************************************************************
;*
;* FUNCTION NAME = GetMonitorType
;*
;* DESCRIPTION   =
;*
;*
;* ENTRY POINT: GetMonitorType
;* LINKAGE:     CALL NEAR
;*
;* INPUT         = NONE
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

GetMonitorType PROC NEAR

;equates for SpeedSTAR 24 supported monitor types or compatibles

MONITOR_NEC2A           equ     0
MONITOR_IBM8514         equ     1
MONITOR_NEC3FGX         equ     2
MONITOR_NEC3FG          equ     3
MONITOR_NEC4FG          equ     4
MONITOR_FIXED           equ     5
MONITOR_CS1572FS        equ     6
MONITOR_CS1024NI        equ     7

;equates for S3 supported monitor types or compatibles                       ;          
S3_IBM8514              equ     0
S3_NEC3FG               equ     1
S3_MAG1448              equ     2
S3_NEC4FG               equ     3
S3_SONY1304             equ     4
S3_NEC5FG               equ     5
S3_RESERVED             equ     6
S3_NEC3FGX              equ     7

UNSUPPORTED_MONITOR     equ     254


        jmp     short getmon_cont

;/*************************************************************************
;* Monitor table for SpeedStar 24x
;**************************************************************************/

                       ;+----------------------------------------- 640x480g
                       ;|       +--------------------------------- 800x600g
                       ;|       |       +------------------------- 1024x768g
                       ;|       |       |       +----------------- 132 col text
                       ;|       |       |       |       +--------- 80 col text
                       ;|       |       |       |       |       +- 40 col text
                       ;|       |       |       |       |       |
fixed_freq:     db    15,      3,      5,      5,     15,      15
nec3fg:         db     2,     14,      5,      5,     15,      15
nec3fgx:        db     2,      6,      8,      5,     15,      15
nec4fg:         db     2,      6,     10,      5,     15,      15
nec5fg:         db     2,      6,     11,      5,     15,      15

;/*************************************************************************
;* Monitor table for SpeedStar 24
;**************************************************************************/

nec2a_24:       db   255,      3,      5,      9
ibm8514_24:     db   255,      3,      5,      9
nec3fg_24:      db     7,      9,      5,      9
nec3fgx_24:     db     7,      6,      7,      9
nec4fg_24:      db     7,      6,      2,      9
fixed_24:       db   255,      7,      4,      9
cs1572fs_24:    db     7,      6,      8,      9
cs1024ni_24:    db   255,      6,      7,      9

;/*************************************************************************  ;          
;* Monitor table for SpeedStar S3
;**************************************************************************/

fixed_freq_s3:
ibm8514_s3:     db     0,      6,      7,      0,      0,       0
nec3fg_s3:      db     2,     14,      5,      0,      0,       0
mag1448_s3:     db     2,      6,     10,      0,      0,       0
nec4fg_s3:      db    11,     10,      8,      0,      0,       0
sony1304_s3:    db     2,      6,     11,      0,      0,       0
nec5fg_s3:      db     2,      6,     11,      0,      0,       0
nec3fgx_s3:     db     2,      6,      8,      0,      0,       0

unknown:        db   UNSUPPORTED_MONITOR

getmon_cont:
        mov     cx,WORD PTR ds:_SVGAHardware     ;          
        cmp     cx,S3_ADAPTER
        jne     @F
        jmp     getmon_S3               ;must be S3
@@:
        mov     cx,WORD PTR ds:_SVGAHardware
        cmp     cx,TSENG_ADAPTER
        jne     getmon_24x              ;must be Western Digital

        mov     cx,WORD PTR ds:_SVGAMonitorType
        cmp     cx,MONITOR_NEC2A
        jne     mon_ibm8514
        lea     di, nec2a_24
        jmp     mon_exit
mon_ibm8514:
        cmp     cx,MONITOR_IBM8514
        jne     mon_nec3fg
        lea     di, ibm8514_24
        jmp     mon_exit
mon_nec3fg:
        cmp     cx,MONITOR_NEC3FG
        jne     mon_nec3fgx
        lea     di, nec3fg_24
        jmp     mon_exit      ;          
mon_nec3fgx:
        cmp     cx,MONITOR_NEC3FGX
        jne     mon_nec4fg
        lea     di, nec3fgx_24
        jmp     mon_exit      ;          
mon_nec4fg:
        cmp     cx,MONITOR_NEC4FG
        jne     mon_fixed
        lea     di, nec4fg_24
        jmp     mon_exit      ;          
mon_fixed:
        cmp     cx,MONITOR_FIXED
        jne     mon_cs1572fs
        lea     di, fixed_24
        jmp     mon_exit      ;          
mon_cs1572fs:
        cmp     cx,MONITOR_CS1572FS
        jne     mon_cs1024ni
        lea     di, cs1572fs_24
        jmp     mon_exit      ;          
mon_cs1024ni:
        cmp     cx,MONITOR_CS1024NI
        jne     unknown_monitor
        lea     di, cs1024ni_24
        jmp     mon_exit      ;          

unknown_monitor:
        lea     di, unknown
        jmp     mon_exit   ;          

getmon_24x:

        call    GetCRTCAddr
        mov     al,2bh
        out     dx,al
        inc     dx
        in      al,dx
        and     al,0f0h
        cmp     al,0f0h
        jne     nec3fg_mon
        jmp     short default_mon

nec3fg_mon:
        cmp     al,70h
        jne     nec3fgx_mon
        lea     di, nec3fg
        jmp     mon_exit    ;          

nec3fgx_mon:
        cmp     al,90h
        jne     nec4fg_mon
        lea     di, nec3fgx
        jmp     mon_exit    ;          

nec4fg_mon:
        cmp     al,0a0h
        jne     nec5fg_mon
        lea     di, nec4fg
        jmp     short mon_exit

nec5fg_mon:
        cmp     al,80h
        jne     default_mon
        lea     di, nec5fg
        jmp     short mon_exit

default_mon:
        lea     di, fixed_freq
        jmp     short mon_exit  ;          

;S3_VRAM part. Not used.
getmon_s3:

        call    unlock_sc       ;unlock sc regs
        call    unlock_s3       ;unlock sc regs

        call    GetCRTCAddr
        mov     al,37h
        out     dx,al
        inc     dx
        in      al,dx
        and     al,60h
        shr     al,5
        cmp     al,S3_IBM8514
        jne     s3_nec3fg_mon
        lea     di, ibm8514_s3
        jmp     short mon_exit

s3_nec3fg_mon:
        cmp     al,S3_NEC3FG
        jne     s3_mag1448_mon
        lea     di, nec3fg_s3
        jmp     short mon_exit

s3_mag1448_mon:
        cmp     al,S3_MAG1448
        jne     s3_nec4fg_mon
        lea     di, mag1448_s3
        jmp     short mon_exit

s3_nec4fg_mon:
        cmp     al,S3_NEC4FG
        jne     s3_sony1304_mon
        lea     di, nec4fg_s3
        jmp     short mon_exit

s3_sony1304_mon:
        cmp     al,S3_SONY1304
        jne     s3_nec5fg_mon
        lea     di, sony1304_s3
        jmp     short mon_exit

s3_nec5fg_mon:
        cmp     al,S3_NEC5FG
        jne     s3_reserved_mon
        lea     di, nec5fg_s3
        jmp     short mon_exit

s3_reserved_mon:
        cmp     al,S3_RESERVED
        jne     s3_nec3fgx_mon
        lea     di, ibm8514_s3
        jmp     short mon_exit

s3_nec3fgx_mon:
        cmp     al,S3_NEC3FGX
        jne     s3_default_mon
        lea     di, nec3fgx_s3
        jmp     short mon_exit

s3_default_mon:                            ;           
        lea     di, fixed_freq_s3

mon_exit:
        ret

GetMonitorType ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = Prog_Clock
;*
;* DESCRIPTION   = Program the speedstar 24 clock using 24 bit data
;*                 for desired clock frequency
;*
;* ENTRY POINT: Prog_Clock
;* LINKAGE:     CALL NEAR
;*
;* INPUT         = NONE
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

Prog_Clock PROC NEAR

        push    bx
        mov     dx,3cch
        in      al,dx
        mov     dx,3c2h

        or      al,8
        mov     cx,6
        cli

kk_1:
        and     al, 0fbh                ;clock low
        out     dx,al
        or      al,4                    ;clock high
        out     dx,al
        loop    kk_1

        and     al, 0fbh
        out     dx,al
        and     al, 0f3h                ;clock low data low
        out     dx,al
        or      al,4                    ;clock high

        out     dx,al
        and     al,0f3h                 ;clock low data low
        out     dx,al
        or      al,4                    ;clock high
        out     dx,al

        mov     bx,8000h                ;program first 16 bits
        call    send_serial
        add     si,2                    ;update pointer
        mov     bx,80h                  ;program last 8 bits
        call    send_serial

        or      al,8
        out     dx,al
        and     al,0fbh                 ;clock low
        out     dx,al
        or      al,4                    ;clock high
        out     dx,al

        sti
        pop     bx
        ret

Prog_Clock ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = Send_Serial
;*
;* DESCRIPTION   = Program the speedstar 24 clock using 24 bit data
;*                 for desired clock frequency
;*
;*                 ENTRY POINT: Send_Serial
;*                 LINKAGE:   CALL FAR
;*
;* INPUT         = NONE
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

Send_Serial PROC NEAR

send_serial_data:

        and     al, 0f7h
        test    word ptr cs:[si],bx
        jnz     send_serial_1
        or      al,8

send_serial_1:

        out     dx,al
        and     al, 0fbh
        out     dx,al

        xor     al,8
        out     dx,al
        or      al,4
        out     dx,al
        shr     bx,1
        jnc     send_serial_data
        ret

Send_Serial ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = GetCRTCAddr
;*
;* DESCRIPTION   = Get CRTC Addr adjusted for color or monochrome
;*
;*                 ENTRY POINT: GetCRTCAddr
;*                 LINKAGE:   CALL FAR
;*
;* INPUT         = NONE
;*
;* RETURN-NORMAL = CRTC Addr in DX
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

GetCRTCAddr PROC NEAR

        push    ax
        mov     dx,3cch
        in      al,dx
        test    al,1
        mov     dx,3d4h
        jnz     get_crtc_1
        mov     dl,0b4h
get_crtc_1:
        pop     ax
        ret

GetCRTCAddr ENDP

;/***************************************************************************            
;*
;* FUNCTION NAME = program_clk_chip_s3
;*
;* DESCRIPTION   = Program the S3 clock using 24 bit data
;*                 for desired clock frequency
;*
;* ENTRY POINT: program_clk_chip
;* LINKAGE:     CALL NEAR
;*
;* INPUT         = NONE
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

program_clk_chip_s3:            ;          
        push    bx
        push    cx
        push    dx

        call    unlock_sc       ;unlock sc regs
        call    unlock_s3       ;unlock sc regs

        mov     ah,40h
        call    in_crtc
        push    ax              ;save crtc reg 40h value
        or      al,1            ;enable enhanced command
        call    out_crtc

        mov     ah,42h          ;use bits 1-0 of enhanced clock select
        call    in_crtc         ;this is done to ensure same state as 3C2h
        and     al,0fch         ;cs1 and cs0
        mov     ah,al

        mov     dx,3cch         ;read misc. out reg
        in      al,dx
        push    ax              ;save misc. out reg value
        and     al,0ch          ;mask off cs1 and cs0
        shr     al,1
        shr     al,1
        or      ah,al           ;same clock selection as misc out.
        mov     al,42h          ;get crtc addr, dx has been changed
        call    GetCRTCAddr     ;ret dx=crtc addr 3x4h
        out     dx,ax
        pop     ax
        push    ax              ;misc out reg value
        or      al,0ch          ;set for enhanced clock
        mov     dx,3c2h
        out     dx,al

        mov     cx,25           ;try 25 times if error
skip_0:
        push    cx
        call    p_clock_s3      ;program 24-bit data
        sub     si,2            ;update pointer to clock data for retry
        pop     cx              ;if error

        mov     ah,43h          ;check for alternate i/o enable
        call    in_crtc         ;to read clock programming error bit
        mov     dx,42e8h
        test    al,10h
        jz      skip_1
        mov     dx,4148h
skip_1:
        in      ax,dx           ;use bit 1 from monitor id bits in
        test    al,40h          ;subsystem  status register for clock
        jnz     skip_2          ;if error
        loop    skip_0
skip_2:
        pop     ax
        mov     dx,3c2h         ;restore misc out reg
        or      al,0ch
        out     dx,al
        pop     ax              ;restore crtc reg 40h value
        call    out_crtc
        mov     ah,42h
        call    in_crtc
        or      al,3            ;force enhaced clock
        call    out_crtc
;       call    lock_s3         ;lock s3 regs
;       call    lock_sc         ;lock sc regs

        call    VGAWait                 ;wait for vertical retrace
        call    VGAWait                 ;wait for vertical retrace

        pop     dx
        pop     cx
        pop     bx
        ret


p_clock_s3:
        mov     ah,42h          ;sc2
        call    in_crtc         ;setup dx for crtc 3x4h
        xchg    al,ah
        push    ax              ;previous vclock selection
                                ;unlock seq
        or      ah,2            ;data high
        mov     cx,6            ;toggle clk 6 times
        cli
kk_1_s3:
        and     ah,0feh         ;clock low
        out     dx,ax
        or      ah,1            ;clock high
        out     dx,ax
        loop    kk_1_s3

        and     ah,0feh
        out     dx,ax
        and     ah,0fch         ;clock low data low
        out     dx,ax
        or      ah,1            ;clock high
        out     dx,ax
        and     ah,0fch         ;clock low data low
        out     dx,ax
        or      ah,1            ;clock high
        out     dx,ax

        mov     bx,8000h        ;16 bits to send
        call    s3_send_serial
        add     si,2
        mov     bx,80h          ;8 bits to send
        call    s3_send_serial
                                ;stop bit
        or      ah,2            ;data_high
        out     dx,ax
        and     ah,0feh         ;clock low
        out     dx,ax
        or      ah,1            ;clock high
        out     dx,ax
        pop     ax
        out     dx,ax           ;restore prev. clock for vertical retrace

        sti
        ret


s3_send_serial:
        and     ah,0fdh         ;send inverted data first
        test    word ptr cs:[si],bx
        jnz     s3_send_serial_1
        or      ah,2
s3_send_serial_1:
        out     dx,ax           ;not data out
        and     ah,0feh         ;clock low
        out     dx,ax

        xor     ah,2            ;now real data out
        out     dx,ax
        or      ah,1            ;clock high
        out     dx,ax
        shr     bx,1            ;next data bit
        jnc     s3_send_serial
        ret

; input  ah     internal register to set
;        dx     port address of indexed register (data = index+1)
; output al     register value
in_crtc:
        call    GetCRTCAddr             ;get crtc address 3x4h
in_crtc_1:
        xchg    al,ah
        out     dx,al
        inc     dx
        jmp     $+2
        xchg    al,ah
        in      al,dx
        dec     dx
        ret

; input  ah     internal register to set
;        al     value to output
;        dx     port of index register (data register must be index + 1)
out_crtc:
        call    GetCRTCAddr             ;get crtc address 3x4h
out_crtc_1:
        xchg    al,ah
        out     dx,al
        inc     dx
        jmp     $+2
        xchg    al,ah
        out     dx,al
        dec     dx
        ret


; unlock s3 extended regs
unlock_s3:
        mov     ax,3848h
        jmp     short out_crtc

; unlock sc extended regs
unlock_sc:
        mov     ax,39a0h
        jmp     short out_crtc

; lock s3 extended regs
lock_s3:
        mov     ax,3800h
        jmp     short out_crtc

; lock sc extended regs
lock_sc:
        mov     ax,3900h
        jmp     short out_crtc

;;;;;;;;;;begin code for stealth 24
;
eeprom_tab_l            equ     20/2            ;10 words
index_m640              equ     0
index_m640h             equ     1
index_m640t             equ     2
index_m800              equ     3
index_m800h             equ     4
index_m800t             equ     5
index_m1024             equ     6
index_m1024h            equ     7
index_m960              equ     8
index_m1280_16          equ     9
index_m1280_256         equ     10
index_m5455             equ     11

COUNTER                 equ     7fh             ; 2K x 1
mon_port                equ     3c2h
mon_bit                 equ     10h
Read_Miscellaneous      EQU     03CCH           ;Misc output read register

Sequencer               EQU     03C4H           ;Sequencer index register

CRT_3B4                 EQU     03B4H           ;Monochrome CRT index addr
CRT_3D4                 EQU     03D4H           ;Monochrome CRT index addr
BT_FIX                  equ     1

;org_5c                  db      0
;---------------------------------------------------------------------------
;       EEPROM Data Format (Each entry 20 bytes)
;       db      0       ;bit7=vpol. bit6=hpol. bit5=interlace
;                        bit4,3   = reserved
;                        bit2,1,0 = entry
;       db      0,0,0   ;clock data (3 bytes)
eeprom_reg_index        label   byte
        db      3bh,0,1,2,3,4,5,6,7,10h,11h,16h,17h,5Dh,33h,0ffh
;
;---------------------------------------------------------------------------
;       input   (al)=eeprom table entry address
;       output  all registers saved except (ax)
upd_crtc_eeprom:
;htotal adjust
        push    bx
        push    cx
        push    dx
        push    ax
        call    Find_6845_Address
        mov     ax,0011h                ;unlock crtc
        out     dx,ax
        pop     ax
        mov     bx,offset eeprom_reg_index
        push    ax              ;(al)=address of eeprom +0
        inc     al              ;(al)=address of eeprom +1
;       inc     al              ;(al)=address of eeprom +2
pp_1:   inc     al              ;(al)=address of eeprom +3
        push    ax
        call    read_eeprom
        mov     cx,ax
        mov     al,cs:[bx]
        cmp     al,0ffh
        jz      pp_2
        mov     ah,cl
        out     dx,ax
        inc     bx
        mov     al,cs:[bx]
        cmp     al,0ffh
        jz      pp_2
        mov     ah,ch
        out     dx,ax
        inc     bx
        pop     ax
        jmp     pp_1

pp_2:   pop     ax
        pop     ax
        push    ax
        call    read_eeprom
        mov     cx,ax
        ;--------------------------------------------;
        ;  update vclk params & interlaced bit       ;
        ;--------------------------------------------;
;set interlace & clock select
        mov     ah,cl
        and     ah,28h          ;entry.5 interlace bit of cr42.5
                                ;entry.3 clk3 bit of cr42.3
        or      ah,3            ;clock select 0,1
        mov     al,42h          ;crtc 42 (clock & interlace)
        out     dx,ax
;set polarity
        push    dx
        mov     dx,3cch
        in      al,dx
        and     al,3fh
        and     cl,0c0h         ;polarity
        or      al,cl
        or      al,0ch          ;clock force to extended
        mov     dx,3c2h
        out     dx,al
        pop     dx
;clock program
        mov     bh,ch           ;1st byte
        pop     ax
        inc     al              ;offset +1
        call    read_eeprom
        mov     cl,al           ;2nd byte
        mov     bl,ah           ;3rd byte
;
        call    program_clk_chip_stl24

        pop     dx
        pop     cx
        pop     bx
        ret
;
;---------------------------------------------------------------------------
;       input   (cl)= 2nd byte, (bh)=1st byte, (bl)= 3rd byte
;       output  bx,cx destroed
;
;;      public  program_clk_chip_stl24
program_clk_chip_stl24 :
        push    ax
        push    dx

        mov     dx,3c4h
        mov     al,1
        out     dx,al
        mov     ah,al
        inc     dx
        in      al,dx
        push    ax
        or      al,20h
        out     dx,al           ;screen off

        call    Find_6845_Address
        mov     ax,0a039h       ;unlock sc regs
        out     dx,ax
        mov     ax,04838h       ;unlock s3 regs
        out     dx,ax

        mov     ch,80h          ;25     ;1000   ;25
skip_0_stl24:
        push    bx
        push    cx
        call    p_clock_stl24
        pop     cx
        pop     bx
        mov     dx,3c2h
        in      al,dx
        test    al,10h
        jnz     skip_2_stl24
        dec     ch
        jnz     skip_0_stl24
skip_2_stl24:

        mov     dx,3c4h
        mov     al,1
        out     dx,al
        inc     dx
        pop     ax              ;screen recover
        out     dx,al           ;LIN fix 12/17/92 should be al ; not ax

        call    wait_vertical_retrace_stl24
        call    wait_vertical_retrace_stl24

        pop     dx
        pop     ax
        ret

;
;---------------------------------------------------------------------------
;public p_clock
p_clock_stl24:
        mov     dx,3cch         ;read misc. out reg
        in      al,dx
        push    ax              ;save value
        or      al,0ch
        mov     dx,3c2h
        out     dx,al
        call    Find_6845_Address
        mov     al,42h          ;sc2
        out     dx,al
        xchg    al,ah
        inc     dx
        in      al,dx
        xchg    al,ah
        dec     dx
        push    ax              ;previous vclock selection

;unlock seq
        or      ah,2            ;data high
        mov     ch,6    ;5      ;toggle clk 5 times
        cli
kk_1_stl24:
        and     ah,0feh         ;clock low
        out     dx,ax
        out     dx,ax
        or      ah,1            ;clock high
        out     dx,ax
        out     dx,ax
        dec     ch
        jnz     kk_1_stl24

        and     ah,0feh
        out     dx,ax
        out     dx,ax
        and     ah,0fch         ;clock low data low
        out     dx,ax
        out     dx,ax
        or      ah,1            ;clock high
        out     dx,ax
        out     dx,ax
        and     ah,0fch         ;clock low data low
        out     dx,ax
        out     dx,ax
        or      ah,1            ;clock high
        out     dx,ax
        out     dx,ax
;
        mov     ch,24
send_serial_stl24:
        and     ah,0fdh         ;send inverted data first
        shl     bx,1
        rcl     cl,1
        jc      send_serial_1_stl24
        or      ah,2
send_serial_1_stl24:
        out     dx,ax           ;not data out
        out     dx,ax
        and     ah,0feh         ;clock low
        out     dx,ax
        out     dx,ax
        xor     ah,2            ;now real data out
        out     dx,ax
        out     dx,ax
        or      ah,1            ;clock high
        out     dx,ax
        out     dx,ax
        dec     ch
        jnz     send_serial_stl24
;stop bit
        or      ah,2            ;data_high
        out     dx,ax
        out     dx,ax
        and     ah,0feh         ;clock low
        out     dx,ax
        out     dx,ax
        or      ah,1            ;clock high
        out     dx,ax
;
        pop     ax
        out     dx,ax           ;restore prev. clock for vertical retrace
        out     dx,ax
;
        pop     ax              ;save value
        mov     dx,3c2h         ;misc. out reg for vclock recover
        out     dx,al
        sti
        ret
;
;----------------------------------------------------------------------------
;        public  wait_vertical_retrace
;
wait_vertical_retrace_stl24:
        push    ax
        push    cx
        push    dx
        call    Find_6845_Address               ;Get CRTC address
        add     dl,6

        mov     cx,0
wv_0:   in      al,dx
        test    al,8
        jnz     wv_1
        loop    wv_0

        mov     cx,0
wv_1:   in      al,dx
        test    al,8
        jz      wv_2
        loop    wv_1

wv_2:   pop     dx
        pop     cx
        pop     ax
        ret
;
;---------------------------------------------------------------------------
        ;input cs:(si) = dat table start address to read
        ;all registers save except (ax)
read_eeprom_all:
        push    si
        push    bx
        push    cx
        push    dx
        mov     bl,00h
vcr_3:  push    bx
        call    read_conf
        mov     cs:[si],ax
        pop     bx
        add     si,2
        inc     bl
        and     bl,COUNTER
        jnz     vcr_3
        pop     dx
        pop     cx
        pop     bx
        pop     si
        ret

;---------------------------------------------------------------------------
        ;input al=adress, unlocked
        ;output ax=data
        ;all registers reserved
read_eeprom:
        push    bx
        mov     bx,ax
        call    read_conf
        pop     bx
        ret
;
;---------------------------------------------------------------------------
        ;input bl=adress
        ;output ax=data
        ;all registers reserved
read_conf:
        push    bx
        push    cx
        push    dx
        call    init_ports      ;video disabled ,crtc 41 bit1 set
                                ;(dx)=3d5/3b5   crtc index = 5ch selected
                                ;(al)=crtc 5ch data
;
        mov     bh,bl
        push    bx
        mov     cx,4
        mov     bx,0110000000000000b
        call    send_data_stl24 ;send read command
        pop     bx

        mov     cx,9
        call    send_data_stl24 ;send address
        mov     cx,16
        mov     bx,0
rc_1:
        and     al,0efh         ;clock low
        call    del_out
        shl     bx,1

        push    dx
        push    ax
        mov     dx,mon_port
        in      al,dx
        test    al,mon_bit
        pop     ax
        pop     dx

        jz      rc_2
        or      bl,1
rc_2:
        or      al,10h          ;clock high
        call    del_out
        loop    rc_1

        and     al,0efh         ;clock low
        call    del_out
;LIN    and     al,0bfh          ;chip select low
;LIN    call    del_out
        call    recover_ports
        mov     ax,bx
        pop     dx
        pop     cx
        pop     bx
        ret

;
;---------------------------------------------------------------------------
init_ports:
        mov     dx,3c4h
        mov     al,1
        out     dx,al
        inc     dx
        in      al,dx
        or      al,20h          ;screen off
        out     dx,al

        mov     dx,3cch
        in      al,dx
        mov     dx,3d4h
        test    al,1
        jnz     init_1
        mov     dx,3b4h
init_1:
        mov     ax,04838h
        out     dx,ax           ;unlock s3
        mov     ax,0a539h
        out     dx,ax           ;unlock sc

        mov     al,5ch
        out     dx,al
        inc     dx
        in      al,dx

        mov     byte ptr [org_5c] ,al           ; save the original cr.5c

        or      al,40h          ;enable chip
        and     al,0cfh
        out     dx,al
        ret
;
;---------------------------------------------------------------------------
recover_ports:
        mov     al, byte ptr [org_5c]          ; recover the original cr.5c
        and     al,0bfh         ;chip select low
        call    del_out         ;out dx(3d5.5c),al

        dec     dx              ;3d4/3b4
        mov     ax,038h
        out     dx,ax           ;lock s3
        mov     ax,039h
        out     dx,ax           ;lock sc

        mov     dx,3c4h
        mov     al,1
        out     dx,al
        inc     dx
        in      al,dx
        and     al,0dfh         ;screen on
        out     dx,al
        ret

        ;input bl=adress, (ax)=data
        ;output  all registers reserved
;write_conf:
;        push    bx
;        push    cx
;        push    dx
;        push    ax              ;data
;        call    init_ports      ;video disabled ,crtc 41 bit1 set
;                                ;(dx)=3d5/3b5   crtc index = 5ch selected
;                                ;(al)=crtc 5ch data
;                                ;out dx(3d5.5c),al
;        mov     bh,bl
;        push    bx
;        mov     cx,12
;        mov     bx,0100110000000000b
;        call    send_data_stl24 ;out dx(3d5.5c),al write enable command
;        call    a_clock
;;       and     al,7fh          ;chip select low
;        and     al,0bfh         ;chip select low
;        call    del_out         ;out dx(3d5.5c),al
;        call    a_clock
;;       or      al,80h          ;chip select high
;        or      al,40h          ;chip select high
;        call    del_out         ;out dx(3d5.5c),al
;        mov     cx,4
;        mov     bx,0111000000000000b
;        call    send_data_stl24 ;out dx(3d5.5c),al  erase command
;        pop     bx              ;address
;        push    bx
;        mov     cx,8
;        call    send_data_stl24
;        call    a_clock
;        and     al,0bfh         ;chip select low
;        call    del_out         ;out dx(3d5.5c),al
;        call    a_clock
;        or      al,40h          ;chip select high
;        call    del_out         ;out dx(3d5.5c),al
;        call    chk_ready
;        mov     cx,4
;        mov     bx,0101000000000000b
;        call    send_data_stl24 ;out dx(3d5.5c),al   write command
;        pop     bx              ;address
;        mov     cx,8
;        call    send_data_stl24 ;out dx(3d5.5c),al
;        mov     cx,16
;        pop     bx              ;data
;        call    send_data_stl24 ;out dx(3d5.5c),al
;        call    a_clock
;        and     al,0bfh         ;chip select low
;        call    del_out         ;out dx(3d5.5c),al
;        call    a_clock         ;clock high to low
;        or      al,40h          ;chip select high
;        call    del_out         ;out dx(3d5.5c),al
;        call    chk_ready
;        mov     cx,12
;        mov     bx,0100000000000000b
;        call    send_data_stl24 ;out dx(3d5.5c),al   write disable command
;        call    a_clock
;        and     al,0bfh         ;chip select low
;        call    del_out         ;out dx(3d5.5c),al
;        call    a_clock
;        call    recover_ports
;        pop     dx
;        pop     cx
;        pop     bx
;        ret

chk_ready:
        push    dx
        push    ax
wait_1: mov     dx,mon_port
        in      al,dx
        test    al,mon_bit
        jz      wait_1
        pop     ax
        pop     dx
        ret

a_clock:
        and     al,0dfh         ;data low
        or      al,10h          ;clock high
        call    del_out
        and     al,0efh         ;clock low
        call    del_out
        ret
;
;---------------------------------------------------------------------------
send_data_stl24:
        and     al,0cfh
        shl     bx,1
        jnc     send_data_1_stl24
        or      al,20h
send_data_1_stl24:
        call    del_out         ;data & clock low
        or      al,10h
        call    del_out         ;clock high
        loop    send_data_stl24
        ret

del_out:
        out     dx,al
        out     dx,al
        out     dx,al
        out     dx,al
        out     dx,al
        out     dx,al
        out     dx,al
        out     dx,al
        out     dx,al
        out     dx,al
        ret
;
;---------------------------------------------------------------------------
Find_6845_Address PROC    NEAR
        PUSH    AX
        MOV     DX,Read_Miscellaneous           ;Get Misc read register
        IN      AL,DX                           ;Read the register
        MOV     DX,CRT_3B4                      ;Set Mono
        TEST    AL,01                           ;0 - MONO 1 - Color
        JE      Set_Mono                        ;Return Mono
        MOV     DX,CRT_3D4                      ;Set Color

Set_Mono:
        POP     AX
        RET
Find_6845_Address ENDP
;
;end code for stealth 24

;/***************************************************************************
;*
;* FUNCTION NAME = GetDACTypeRtn
;*
;* DESCRIPTION   = Diamond specific DAC routine
;*
;*                 ENTRY POINT: GetDACTypeRtn
;*                 LINKAGE:   CALL FAR
;*
;* INPUT         = NONE
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

GetDACTypeRtn  PROC NEAR

        push    bx
        push    dx

        call    cmd_off
        in      al,dx

ddd_1:
        mov     ah,al
        in      al,dx
        cmp     al,ah
        jnz     ddd_1
        mov     bl,al

        mov     ah,8
        call    cmd_on
        cmp     al,8eh
        jz      music_dac

ddd_2:

        in      al,dx
        cmp     al,8eh
        jz      music_dac
        dec     ah
        jnz     ddd_2

        call    cmd_on
        in      al,dx
        mov     bh,al
        call    cmd_off
        mov     al,0ffh
        out     dx,al

        mov     ah,055h

ddd_3:
        call    cmd_on
        mov     al,ah
        out     dx,al
        call    cmd_off
        in      al,dx
        cmp     al,ah
        sub     ah,ah

ddd_4:
        call    cmd_on
        mov     al,bh
        out     dx,al
        call    cmd_off
        mov     al,bl
        out     dx,al

ddd_5:
        pop     dx
        pop     bx
        ret

music_dac:
        call    cmd_off
        mov     ah,2
        jmp short ddd_5

;enable access to dac command register

cmd_on:
        mov     dx,3c9h
        in      al,dx           ;reset sequencer
        mov     dx,3c6h
        in      al,dx
        jmp     $+2
        in      al,dx
        jmp     $+2
        in      al,dx
        jmp     $+2
        in      al,dx
        ret

;disable access to dac command register

cmd_off:
        mov     dx,3c9h
        in      al,dx           ;reset sequencer
        mov     dx,3c6h
        ret

GetDACTypeRtn  ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = SetDACCMDReg
;*
;* DESCRIPTION   = Diamond specific DAC routine
;*
;*                 ENTRY POINT: SetDACCMDReg
;*                 LINKAGE:   CALL FAR
;*
;* INPUT         = NONE
;*
;* RETURN-NORMAL =
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

SetDACCMDReg PROC NEAR

        push    ax
        call    GetDACTypeRtn
        or      ah,ah
        jz      set_cmd_ret
        cmp     ah,2
        jz      set_cmd_music

        call    cmd_on
        pop     ax
        out     dx,al
        call    cmd_off

set_cmd_ret:
        ret

set_cmd_music:

        mov     ah,10h
        call    cmd_on
        cmp     al,8eh
        jz      qqq_3

qqq_2:
        in      al,dx
        cmp     al,8eh
        jz      qqq_3
        dec     ah
        jnz     qqq_2
        pop     ax
        ret

qqq_3:

        pop     ax
        out     dx,al
        ret

SetDACCMDReg ENDP

IF DEBUG

;/*
;** Equates for com ports
;*/

COM1_DAT=       03f8H           ; base for COM1
COM2_DAT=       02f8H           ; base for COM2
R_IEN   =       1               ; Interrupt enable
R_IER   =       2               ; interrupt ID
R_LCR   =       3               ; line control registers
R_MCR   =       4               ; modem control register
R_LSR   =       5               ; line status register
R_MSR   =       6               ; modem status regiser
R_DLL   =       0               ; divisor latch least sig
R_DLM   =       1               ; divisor latch most sig

; Exactly one of the next two lines should be uncommented to select port
;UR_DAT =       COM2_DAT        ; select COM2 = 02f8H
UR_DAT  =       COM1_DAT        ; select COM1 = 03f8H
UR_IEN  =       UR_DAT+1        ; Interrupt enable
UR_IER  =       UR_DAT+2        ; interrupt ID
UR_LCR  =       UR_DAT+3        ; line control registers
UR_MCR  =       UR_DAT+4        ; modem control register
UR_LSR  =       UR_DAT+5        ; line status register
UR_MSR  =       UR_DAT+6        ; modem status regiser
UR_DLL  =       UR_DAT          ; divisor latch least sig
UR_DLM  =       UR_DAT+1        ; divisor latch most sig


;/***************************************************************************
;*
;* FUNCTION NAME = DPRINTF - Debug Printf
;*
;* DESCRIPTION   =
;*
;*      Dprintf is a kernel debug print formatting package.
;*      This version is a hastily stripped version of another routine
;*      with the same name.
;*      Due to difficulties passing variable length argument lists
;*      through a call gate transition, only one argument, a zero
;*      terminated string is passed to this routine.  The string
;*      will be sent to either COM1 or COM2 depending on the definition
;*      of UR_DAT.
;*
;*      About the only intelligent thing this routine can do is process
;*      XON/XOFF characters from the equipment attached to the debug
;*      port so that the output will not overrun the recieving device.
;*      However after recieving an XOFF, this routine will simply spin
;*      in a loop waiting for XON.  Using equipment/software on that
;*      can keep up on the debug port is recommended.
;*
;*      The format string is an ASCIZ string which can contain
;*      literal characters.
;*
;*      Literal characters
;*          - any character not part of a format specification.  Special
;*            non-printing characters are:
;*              \n      - CRLF
;*              \t      - tab
;*              \b      - bell
;*              \\      - \
;*
;*      WARNINGS
;*          As befitting a debug routine, DPRINTF does not have a whole lot
;*          of "failsafe" code in it.
;*
;* INPUT         = (sp+6  ) = segment of string
;*                 (sp+4  ) = offset of string
;*                 (sp+2  ) = seg of return address
;*                 (sp    ) = offset of return address
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

DPRINTF PROC    far

        push    bp
        mov     bp,sp
        push    ds
        push    es
        push    di
        push    si
        push    dx
        push    cx
        push    bx
        push    ax
        cld

;/*
;** Change string address to far ptr on stack
;*/
        add     bp,6
        mov     si,[bp]
        add     bp,2
        mov     ax,[bp]
        push    ax
        pop     ds

;/*
;**     Scan format string for next character
;**
;**     (ds:si) = address of format string
;**     (ss:bp) = address of next argument
;*/

public dpf1
dpf1:   lodsb                   ; (al) = format string byte
        and     al,al
        je      dpf3            ; all done
        cmp     al,'\'
        jnz     dpf2            ; got the character

;/*
;**       it's an "\" escape code - crack the argument character
;*/

        lodsb
        and     al,al
        je      dpf3            ; all done, ignore hanging \
        xchg    ah,al
        mov     al,0Ch
        cmp     ah,'n'
        jne     dpf1$5          ; not \n
        mov     al,0dH
        call    putchar
        mov     al,0aH
        jmp     SHORT dpf2      ; print LF

dpf1$5: cmp     ah,'t'
        mov     al,9
        je      dpf2            ; is \t
        cmp     ah,'b'
        mov     al,7
        je      dpf2            ; is \b
        xchg    ah,al
dpf2:   call    putchar
        jmp     dpf1

;/*
;**       have the end of the format string - exit
;*/

dpf3:   pop     ax
        pop     bx
        pop     cx
        pop     dx
        pop     si
        pop     di
        pop     es
        pop     ds
        pop     bp
        ret     4       ; pop 4 bytes after return address

DPRINTF ENDP

;/***************************************************************************
;*
;* FUNCTION NAME = inchr
;*
;* DESCRIPTION   = input character
;*
;* INPUT         = NONE
;* OUTPUT        = 'z' set if no character
;*                 'z' clear if char
;*                   (al) = char
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

IF 1
inchr   PROC NEAR
        ret
inchr   ENDP
ELSE
inchr   PROC NEAR
        mov     dx,UR_LSR
        in      al,dx
        and     al,1
        jz      inchr1
        mov     dx,UR_DAT
        in      al,dx
        and     al,07fh
inchr1: ret
inchr   ENDP
ENDIF

;/***************************************************************************
;*
;* FUNCTION NAME = putchar
;*
;* DESCRIPTION   = put a character on the console
;*
;* INPUT         = (al) = character
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

putchar PROC    NEAR
        pushf
        cli
        push    dx
        push    cx
        push    bx
        push    ax              ; (al) = character

;/*
;**     see if CTL-Q or CTL-S
;*/

        pushf
        cli
        call    inchr
        jz      putc3           ; no characters incomming
        cmp     al,19           ; ctl-S?
        jnz     putc3           ; no, ignore

;/*
;**     have ctl-s.  wait till we see ctl-Q
;*/

putc2:  call    inchr
        jz      putc2
        cmp     al,17
        jnz     putc2

putc3:  popf
        mov     dx,UR_LSR
putc4:  in      al,dx
        test    al,020h
        jz      putc4

;/*
;**     ready.  crank it out!
;*/

        mov     dx,UR_DAT

        pop     ax
        out     dx,al

        pop     bx
        pop     cx
        pop     dx
        popf
        ret
putchar ENDP

ENDIF   ;DEBUG

R2SEG   ENDS
END

