;*DDK*************************************************************************/
;
; 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 80,132
;*******************************************************************************
;
; MODULE NAME: XGARING0.ASM
;
; DESCRIPTION: OS/2 kernel device driver support for XGA hardware.
;              The entry points are reached through a DOSDevIOCtl call.
;
;              Includes support for:
;                 - address lock / unlock / verify
;                 - virtual to physical conversion
;                 - Expressway initialisation
;
; CHANGE HISTORY:
;
;   17Sep92 KJE - DCR53004 (WPS Implementation for INT 10 DMQS Services).
;
;*******************************************************************************
.386p                              ;Protect mode 386 code

HARD_DRAW       EQU     1          ; get the real XGA register defns
RING0           EQU     1          ; get special ring0 versions
INCL_GPIBITMAPS EQU     1

include os2.inc
include xgaadapt.inc
include eddhcone.inc
include eddhmacr.inc
include xgaring0.inc
include edd0extf.inc               ; C functions, in edd0code.c

;*******************************************************************************
; XGA instance structure
;*******************************************************************************
EXTRN instances : BYTE
EXTRN PreModeData : BYTE
EXTRN PostModeData : BYTE

;*******************************************************************************
; Initialization functions
;*******************************************************************************
EXTRN Init : NEAR

;*******************************************************************************
; Data Segment
;*******************************************************************************
AKDATA SEGMENT USE16 WORD PUBLIC 'FAR_DATA'

;*******************************************************************************
; Device driver header...
; This MUST be the first item in the data segment.
;*******************************************************************************
DrvrHdr LABEL WORD

   DW     -1                                  ;device header ptr
   DW     -1                                  ;device header ptr
   DW     Dev_Chr_Dev + Devlev_1 + Dev_30     ;attributes
   DW     OFFSET Strategy                     ;strategy routine offset
   DW     0                                   ;reserved
   DB     '$$$XGA20'                          ;device name
   DW     4 dup (0)                           ;reserved words

include tables.asm

public     FlatPageList                       ; dd
public     PageList                           ; dd[33]
public     FlatLockHandle                     ; dd
public     LockHandle                         ; db[12]

FlatPageList          DD     0
PageList              DD     33 DUP (0)
FlatLockHandle        DD     0
LockHandle            DB     12 DUP (0)

;*******************************************************************************
; ABIOS call data used during POS reading
;*******************************************************************************
requestBlock     Request_Block <>             ;ABIOS function request data

public vRamSpace
vRamSpace             DW 0

public XGARegPointer
public FlatXGARegPointer
public pXGAReg
XGARegPointer         DD     0
FlatXGARegPointer     DD     0
pXGAReg               DD     0

;*******************************************************************************
; Mode information passed from the BVH.
; Pointer to the BVHs mode infomation
;*******************************************************************************
pBVHModeDataInfo      DD     0
savemode1             DB     0

;*******************************************************************************
; XOR with these to get vH Vh vh VH , assumes was init to vH
;*******************************************************************************
settings              DB     0, 0C0h, 80h, 0C0h
adders                DB     0,4,8,12,1,5,9,13,2,6,10,14,3,7,11,15

numberoftries         EQU    200
idarea                DD     numberoftries DUP (0)
resetcount            DW     numberoftries
repcount              DW     numberoftries
pointer               DW     0

;*******************************************************************************
; These globals are used by DoPhysToUVirt and functions that call it.
;*******************************************************************************
public ulPhysicalAddress
public usSize
public pResult
public fInt10DMQSData
ulPhysicalAddress     DD 0
usSize                DW 0
pResult               DD 0
fInt10DMQSData        DD 0

AKDATA  ENDS

_TEXT SEGMENT USE16 WORD PUBLIC 'CODE'
ASSUME  CS:_TEXT, DS:AKDATA, ES:NOTHING, SS:NOTHING

;*******************************************************************************
; Function: STRATEGY
;
; Purpose:  Device driver entry point (Kernel mode).
;
;           OS/2 enters here on all Device requests with es:bx pointing
;           to the request packet.
;*******************************************************************************
PUBLIC Strategy
Strategy PROC FAR

; Get packet command code
     mov     al, BYTE PTR es:[bx].PktCmd     ;get request packet code
     cbw
     mov     di, ax

; If command code > maximum command
     cmp     di, MAX_CMD_CODE          ;code type supported ?
     jbe    code_call               ;PROCess request if so

; Set command error = Unknown command
     mov     ax, UNKNOWN_COMMAND          ;set illegal call
     jmp     short Home               ;go set packet status

; call strategy routine
code_call:
     push     es                    ;protect packet pointer
     push     bx
     push     fs                    ; driver must not change these!
     push     gs

     add     di,di
     call     WORD PTR Strategy_jTab [di]     ;go do request

     pop     gs
     pop     fs
     pop     bx                    ;get packet pointer back
     pop     es

Home:
     mov     es: [bx].PktStatus, ax          ; pass back any error code
     ret

Strategy ENDP

;*******************************************************************************
; Function:     Generic IOctl routine.
;
; Purpose:
;
; All calls to the XGA code are routed through here by the strategy routine.
; To access the routines the user does a DosDevIOCtl call.
;*******************************************************************************
PUBLIC IOctl
IOctl PROC NEAR

; If Function category and Function code are supported
     cmp     es: [bx].GIOCatFunc, (GEN_FUNCTION shl 8) or XGA_CATEGORY
                                       ;check request type
     jne     unsupported               ;go home if no good

; If request code > maximun request code
     lfs     si, dword ptr es:[bx].GIOParaPack
     mov     si, word ptr fs:[si]
     cmp     si, MAX_REQ_CODE          ;check request type
     jbe     reqOk                     ;skip if OK

; Set index error
     mov     ax, BADINDEXERR           ;set error type
     jmp     IOctl_exit

reqOk:

;*******************************************************************************
; Call appropriate request
; All routine return TRUE for success or FALSE for failure
;*******************************************************************************
     add     si,si
     call    WORD PTR RequestTable[si] ;go do the request

Check_error:
     and     ax, ax                    ;anything dodgy ?
     jz      drvErr                    ;signal error if so

     mov     ax, NO_ERROR              ;set OS2 return code
     jmp     IOctl_exit

drvErr:
     mov     ax, GENERAL_FAILURE       ;tell OS/2   ...

IOctl_exit:
     ret

IOctl ENDP

;*******************************************************************************
; Function:  LOCKPM
;
; Purpose:   Locks all segments specified in parm block, and
;            returns physical address and lock handle for each one.
;            On errors physical address is set to 0 and handle contains
;            error code for debug.
;            verify access failed = 8006h
;            lock failed          = 8005h
;            virt to phys failed  = 8008h
;
; Entry:     On entry es:[bx] points to DevIoclt parm packet
;
;*******************************************************************************
PUBLIC  lockpm
lockpm  PROC NEAR

; Get address of parm block from DevIoctl data packet
          lgs     di, es:[bx].GIODataPack

; Do for number of entries
          mov     cx, gs:[di].pmlock_count ;get no. of things to lock
          add     di, 2

next_lock:
          les     si, gs:[di]           ;get addr of next bitmap_struc

; If virtual address is valid
          mov     ax, es:[si].virt_addr.hi ;get virt addr selector
          and     ax, ax                ;if not valid virt addr
          jz     lock_loop           ;skip this one

; Verify virtual address
          push     cx                ;save loop count
          push     di
          mov     ecx,es:[si].bitmap_size  ;verify whole length
          mov     di, es:[si].virt_addr.lo ;get virt addr offset
          mov     dh, VERIFYREAD           ;read access only
          mov     dl, DevHlp_VerifyAccess  ;verify access
          call     Device_Help           ;verify access
          pop     di
          pop     cx

; If Verify error
          jnc     verify_ok

; Set error and return
          mov     es:[si].lock_handle.lo, verfyerr
          jmp     lock_exit

verify_ok:

; Lock virtual address
          mov     bx, es:[si].lock_type      ;set lock type
          shl     bx, 8
          mov     dl, DevHlp_Lock
          call     Device_Help           ;lock up segment

; If Lock error
          jnc     lock_ok

; Set error and return
          mov     es:[si].lock_handle.lo, lockerr
          jmp     lock_exit

lock_ok:

; Save lock handle in parm block
          mov     es:[si].lock_handle.lo, bx
          mov     es:[si].lock_handle.hi, ax

; Get physical address from virtual
          push     si                ; 
          push     es                ; 
          push     ds
          push     ds
          lds     si, es:[si].virt_addr      ;load virt addr
          mov     dl, DevHlp_VirtToPhys
          pop     es
          call     es:Device_Help           ;do virtToPhys call
          pop     ds
          pop     es                ;restore parm block offset
          pop     si                ;restore parm block offset

; If Virt to Phys error
          jnc     virt_phys_ok

; Set error and return
          mov     es:[si].lock_handle.lo, virt2physerr
          jmp      lock_exit

virt_phys_ok:

; Save physical address in parm block
          mov     es:[si].phys_addr.lo, bx
          mov     es:[si].phys_addr.hi, ax

lock_loop:

; Increment to next segment in parm block
          add     di, 4                ;next bitmap_struc pointer
          dec     cx
          jcxz     lock_exit
          jmp     next_lock           ;till parm block done

lock_exit:
          mov     ax, TRUE
          ret

lockpm  ENDP

;*******************************************************************************
; Function:  UNLOCKPM
;
; Purpose:   Unlocks all segs with handles specified in parm block, and
;            sets handles to zero if successful.
;
; Entry:     On entry es:[bx] points to DevIoclt parm packet
;
;*******************************************************************************
PUBLIC unlockpm
unlockpm PROC NEAR

; Get address of parm block from DevIoctl data packet
          les     si, es:[bx].GIODataPack

; Do for number of entries
          mov     cx, es:[si].pmunlock_count     ;get no. of handles
          add     si, 2                      ;point to first handle

next_unlock:

; If handle <> 0 (valid handle)
          mov     ax, es:[si+2]             ;get handle hi
          mov     bx, es:[si]             ;and handle lo
          mov     dx, ax
          or     dx, bx                  ;handle valid ?
          jz     unlock_loop             ;skip if not

; Unlock Handle
          mov     dl, DevHlp_Unlock
          call     Device_Help             ;release memory lock

; If unlock error
          jnc     unlock_ok

; Do next handle
          jmp     unlock_loop

unlock_ok:

; Set handle = 0
          mov     dword ptr es:[si], 0

unlock_loop:

; Increment to next handle
          add     si, 4                ;next handle
          loop     next_unlock           ;till parm block done

unlock_exit:
          mov     ax, TRUE
          ret

unlockpm ENDP

;*******************************************************************************
; Function: QueryAdapterInfo
;
; fills in the ADAPTERINFO structure pointed to by the
; paramter passed in using the current XGA instance data.
;*******************************************************************************
PUBLIC QueryAdapterInfo
QueryAdapterInfo PROC NEAR

; get a pointer to the return data area - param in edd0code.c
          mov     eax, dword ptr es:[bx].GIODataPack

          push     eax        ; this is where the input/output data is
          call     return_adapterinfo

          ret

QueryAdapterInfo ENDP

;*******************************************************************************
; Function: QueryAdapterInfo2
;
; fills in the ADAPTERINFO2 structure pointed to by the
; paramter passed in using the current XGA instance data. This is seperate
; to QueryAdapterInfo because QueryAdapterInfo is called by other programs
; (external to the driver) and so we can not change the ADAPTERINFO
; structure.
;*******************************************************************************
PUBLIC QueryAdapterInfo2
QueryAdapterInfo2 PROC NEAR

; Get a far pointer to the ADAPTERINFO2 structure that we must fill in.

          les     di, dword ptr es:[bx].GIODataPack

          mov     ax, instances[0].usScreenSizeX
          mov     es:[di].usScreenWidth, ax

          mov     ax, instances[0].usScreenSizeY
          mov     es:[di].usScreenHeight, ax

          mov     ax, instances[0].usMonitorTypeInstance
          mov     es:[di].usMonitorType, ax

          ret

QueryAdapterInfo2 ENDP

;***********************************************************************
; Function:  copy_vram
;
;
; Purpose:   copies 64K of vram to system memory or vice versa
; registers: eax   32-bit system memory physical address
;            ebx   32-bit vram physical address
;            es:di hardware info pointer
;            edx   pixel operation to use
;
; VRAM is copied as if it was 8bpp - this is fine as long as this is
; the only routine to use this memory, otherwise we will get problems
; with intel/motorola conversions occuring.
;
;***********************************************************************
PUBLIC copy_vram
copy_vram  PROC NEAR

; get the base address of the hardware registers
     movxga     es:[di].XGA_mem_map_virt

; ensure the hardware is not busy
     waitshort

; set foreground and background mixes to overpaint
     memregwrite     fg_mix, HWMIX_SOURCE
     memregwrite     bg_mix, HWMIX_SOURCE

; select the destination map
     memregwrite     hw_pi_map_index, SEL_PIX_MAP_A

; write destination physical address to hardware mapA base
     memregwrite     hw_pi_map_base_ptr, eax

; write mapA width and height -> 64K pixels @ 8 bits per pixel
     memregwrite     hw_pi_map_width,  0ffh
     memregwrite     hw_pi_map_height, 0ffh

; write mapA format - 8bpp
     memregwrite     hw_pi_map_format, <EIGHT_BPP OR MOTOROLA>

; select the source map
     memregwrite     hw_pi_map_index, SEL_PIX_MAP_B

; write source physical address to hardware mapB base
     memregwrite     hw_pi_map_base_ptr, ebx

; write mapB width and height: same as for target
     memregwrite     hw_pi_map_width,  0ffh
     memregwrite     hw_pi_map_height, 0ffh

; write mapB format
     memregwrite     hw_pi_map_format, <EIGHT_BPP OR MOTOROLA>

; blt the entire map
     memregwrite     dest_map, 0
     memregwrite     src_map,  0

     memregwrite     dim1, 0ffh
     memregwrite     dim2, 0ffh

; write the pixel op to kick off the blt:
     memregwrite     pixel_op, edx

; could really do a waitlong here!
     waitshort

     ret

copy_vram  ENDP

;*****************************************************************************
; Function:   lock_seg
;
; Purpose:    locks a segment
;
; Registers:  ax  selector of segment to lock
;
; Returned:   carry set if failure
;             carry clear if successful and
;             eax = physical address
;             ecx = lock handle
;*****************************************************************************
PUBLIC lock_seg
lock_seg PROC NEAR

; lock virtual address; ax = selector
          push     ax
          xor     bx, bx                ; short term lock
          mov     dl, DevHlp_Lock
          call     Device_Help
          jc     ls_err_exit

; save lock handle
          mov     cx, ax
          shl     ecx, 10h
          mov     cx, bx

; get physical address from virtual
          pop     ax
          push     si
          push     ds
          mov     ds, ax
          xor     si, si                ; ds:si = virtual address
          mov     dl, DevHlp_VirtToPhys
          call     es:Device_Help
          pop     ds
          pop     si

; if Virt to Phys error
          jc     ls_exit

; get 32-bit address in eax
          shl     eax, 10h
          mov     ax, bx
          clc

ls_exit:
          ret

ls_err_exit:
          pop     ax
          ret

lock_seg ENDP

;*****************************************************************************
; Function:   unlock_seg
;
; Purpose:    unlocks a segment
;
; Registers:  eax  lock handle
;
; Returned:   carry set if failure
;             carry clear if successful
;*****************************************************************************
PUBLIC unlock_seg
unlock_seg PROC NEAR


          mov     bx, ax
          shr     eax, 10h
          mov     dl, DevHlp_Unlock
          call     Device_Help
          ret

unlock_seg  ENDP

;*******************************************************************************
; Function:  save_vram
;
; Purpose:   copies all VRAM to system memory and returns a pointer to it
;*******************************************************************************
app_huge_shift     equ     ss:[bp-12h]
blt_direction      equ     ss:[bp-10h]
vram_base_add      equ     ss:[bp-0ch]
seg_to_lock        equ     ss:[bp-8]
num_segs           equ     ss:[bp-6]
hlock              equ     ss:[bp-4]

PUBLIC  save_vram
save_vram PROC NEAR

          push     bp
          mov     bp, sp
          sub     sp, 10h

; set up the VRAM copy direction in si
          mov     dword ptr blt_direction, FROM_VRAM

save_restore_common:
          push     ecx
          push     esi
          push     edi

; get a pointer to the return data area
          lfs     si, es:[bx].GIODataPack

; get the size of the huge shift
          mov     ax, fs:[si].huge_shift
          mov     app_huge_shift, ax

; get addressability to the hardware info
          mov     ax, SEG instances
          mov     es, ax
          mov     di, OFFSET instances

; get vram base address
          mov     eax,es:[di].XGA_base_vram
          mov     vram_base_add, eax

; get hold of the VRAM size
          mov     eax, es:[di].XGA_vram_size

; get the number of 64K segments in the VRAM
          shr     eax, 10h
          mov     num_segs, ax

; now lock a segment...
; lock virtual address
          mov     ax, fs:[si].saved_buffer.hi   ; segment #
          mov     seg_to_lock, ax
sv_next_seg:
          call     lock_seg
          mov     dword ptr hlock, ecx

; if Virt to Phys error
          jc     sv_err

; do the copy
          mov     edx, dword ptr blt_direction
          mov     ebx, dword ptr vram_base_add
          call     copy_vram

; unlock the segment
          mov     eax, dword ptr hlock
          call     unlock_seg
          jc     sv_trap_err

; more to do?
          dec     word ptr num_segs
          jz     sv_all_copied
          mov     ax, seg_to_lock
          add     ax, app_huge_shift
          mov     seg_to_lock, ax
          add     dword ptr vram_base_add, 10000h
          jmp     sv_next_seg

sv_all_copied:
          mov     ax, TRUE

sv_exit:
          pop     edi
          pop     esi
          pop     ecx

          mov     sp, bp
          pop     bp
          ret

sv_trap_err:
          int     3

sv_err:
          mov     ax, FALSE
          jmp     sv_exit

save_vram ENDP

;*******************************************************************************
; Function:  restore_vram
;
; Purpose:   copies contents of the passed system memory buffer to vram
;*******************************************************************************
app_huge_shift     equ     ss:[bp-12h]
blt_direction      equ     ss:[bp-10h]
vram_base_add      equ     ss:[bp-0ch]
seg_to_lock        equ     ss:[bp-8]
num_segs           equ     ss:[bp-6]
hlock              equ     ss:[bp-4]

public restore_vram
restore_vram PROC NEAR

          push     bp
          mov     bp, sp
          sub     sp, 10h

; set up the VRAM copy direction in si
          mov     dword ptr blt_direction, TO_VRAM

          jmp     save_restore_common

restore_vram     ENDP

;*******************************************************************************
; Function: set_vga_mode
;
; Purpose:  put the coprocessor into vga mode
;*******************************************************************************
PUBLIC set_vga_mode
set_vga_mode PROC NEAR

     ; get addressability to the hardware info
     mov     ax, SEG instances
     mov     es, ax
     mov     bx, OFFSET instances

     ; get the io register base
     mov     dx, word ptr es:[bx].XGA_io_base

     ; aperture control
     inc     dx
     xor     al, al
     out     dx, al

     ; interrupt disable
     add     dx, 03h
     out     dx, al

     ; clear interrupts
     inc     dx
     mov     al, 0ffh
     out     dx, al

     ; reference the index register
     mov     dx, word ptr es:[bx].XGA_io_base
     add     dx, index

     ; set hardware palette mask = 1's (vga default)
     mov     ax,0ff64h
     out     dx, ax

     ; VFB enabled
     ; clear video clock error
     ; screen blanked prepare for reset
     mov     ax, 01550h
     out     dx, ax

     ; enable VFB, screen blanked CRTC reset
     mov     ax, 01450h
     out     dx, ax

     ; normal scale factors (x1)
     mov     ax, 051h
     out     dx, ax

     ; select vga oscillator (osc in VGA 9 pixel mode)
     mov     ax, 0454h
     out     dx, ax

     ; ext oscillator (vga)
     mov     ax, 070h
     out     dx, ax

     ; ensure no vsync interrupts
     mov     ax, 0202ah
     out     dx, ax

     ; set hardware operating mode to vga
     mov     dx, word ptr es:[bx].XGA_io_base
     mov     al, 01h
     out     dx, al

     ; enable vga address decode
     mov     dx, 03c3h
     mov     al, 01h
     out     dx, al

set_vga_exit:
     mov     ax, TRUE
     ret

set_vga_mode ENDP

;*******************************************************************************
; Function: set_native_mode
;
; Purpose:  put the coprocessor into native mode
;           and program the CRTC registers for a particular display
;           mode configuration
;*******************************************************************************
PUBLIC set_native_mode
set_native_mode PROC NEAR

     push     ecx
     push     si

     ; get the pointer to our buffer
     les     bx, es:[bx].GIODataPack

     ; get the mode
     mov     bx, word ptr es:[bx]

     ; convert to an index into table of words
     add     bx,bx

     ; get the start of that data from the table
     mov     si, native_mode_data_table[bx]

     ; get the io register base
     mov     dx, instances.XGA_io_base
     mov     cx, dx

     ; get the xga register id from the table
     mov     ax, [si].reg_id

next_out:
     ; check if the register id is indexed
     test     ax, INDEXED_REG
     jz     not_indexed

     ; set dx to the index register
     add     dx, index

     ; get the data to be written in ah - index is in al
     mov     ah, byte ptr [si].reg_data

     jmp     output_the_data

not_indexed:
     ; get the register id into dx ready for an out
     add     dx, ax

     ; get the data for the out into ax
     mov     ax, [si].reg_data

output_the_data:
     out     dx, ax

     ; recover the io_register base
     mov     dx, cx

     ; advance the pointer to our data - table is table of twin words
     add     si, 4

     ; get the xga register id from the table
     mov     ax, [si].reg_id

     ; if more still to do, go do it
     cmp     ax, TERMINATOR
     jnz     next_out


     ; We are leaving palette loading up to the caller. If it is ever
     ; made part of this function, this is where it happens.


     ; clear out the entire VRAM to prevent any garbage appearing
     mov     eax, instances.XGA_base_vram
     mov     ecx, instances.XGA_vram_size
     movxga     instances.XGA_mem_map_virt

     memregwrite     hw_pi_map_index, SEL_PIX_MAP_B
     memregwrite     hw_pi_map_base_ptr, eax

     ; Set hardware MapB Width = dimension 1 = 1024
     mov     ax, 3ffh
     memregwrite     hw_pi_map_width, ax
     memregwrite     dim1, ax


     ; Set hardware MapB Height = dimension 2 = VRAM size / 1024
     shr     ecx, 0ah
     dec     cx
     memregwrite     hw_pi_map_height, cx
     memregwrite     dim2, cx

     ; Set hardware MapB Format = 8bpp
     memregwrite     hw_pi_map_format, EIGHT_BPP

     ; Set hardware Foreground colour = 0's
     memregwrite     fg_colour, 0h

     ; Set hardware Foreground mix = overpaint
     memregwrite     fg_mix, HWMIX_SOURCE

     ; Set hardware Destination map x/y = 0
     memregwrite     dest_map, 0

     ; Ensure the pixel mask has all bits enabled for update
     memregwrite     plane_mask, 0ffffh

     ; Disable destination update inhibition (ie. allow updates)
     memregwrite     colour_comp_fun, 04h

     ; Set hardware Pixel Operation to:-
     ;  Bg source = xxxx
     ;  Fg source = Fg register
     ;  Step      = PxBlt
     ;  Source    = xxxx
     ;  Dest      = MapB
     ;  Patt      = Fg fixed
     ;  Mask      = disabled
     ;  Draw mode = xxxx
     ;  Oct          = 000
     memregwrite     pixel_op, solid_blt_b

; this could be a waitlong!
     waitshort

snm_blt_complete:
     ; Now set the border colour to its initial value and set all
     ; bits in the palette mask.
     ; dx still has the io register base
     add     dx, index

     mov     al, border_colour
     xor     ah, ah
     out     dx, ax

     mov     al, palette_mask
     mov     ah, 0ffh
     out     dx, ax

     sub     dx, index          ; restore the base pointer

; make sure the pc_vram window is zero
     add     dx, pc_vram_indx
     xor     al,al
     out     dx,al
     sub     dx, pc_vram_indx

native_exit:
     mov     ax, TRUE
     pop     si
     pop     ecx
     ret

set_native_mode ENDP

;*******************************************************************************
; Function: SetRegisters
;
;     es - segment containing register triplet data
;     si - first byte of register triplet data
;     cx - last byte of register triplet data
;*******************************************************************************
PUBLIC SetRegisters
SetRegisters PROC NEAR

SR_set_regs_loop:
     ; While we have not reached the end of the mode data...
     cmp     si, cx
     jg     SR_set_regs_finished

     ; Get the XGA instances register IO base into dx.
     mov     dx, instances[0].XGA_io_base

     ; See what type of register we are writing to.
     movzx     di, byte ptr es:[si]
ifdef FIREWALLS
     ; We expect a register type of 0 to 5.
     cmp     di, 5
     jle     @F
     int     3
@@:
endif ; FIREWALLS

     ; Jump to the code specific to this register type.
     add     di, di
     mov     di, SetRegisters_jump_table[di]
     jmp     di

SR_Direct:
     ; Get the address of the direct register and write the data to it.
     movzx     ax, byte ptr es:[si+1]
     add     dx, ax
     mov     al, es:[si+2]
     out     dx, al
     jmp     SR_set_reg

SR_Direct_ORed:
     ; Get the address of the direct register and OR the data to it.
     movzx     ax, byte ptr es:[si+1]
     add     dx, ax
     in     al, dx
     or     al, es:[si+2]
     out     dx, al
     jmp     SR_set_reg

SR_Direct_ANDed:
     ; Get the address of the direct register and AND the data to it.
     movzx     ax, byte ptr es:[si+1]
     add     dx, ax
     in     al, dx
     and     al, es:[si+2]
     out     dx, al
     jmp     SR_set_reg

SR_Indexed:
     ; Get the address of the index register and write the data to it.
     add     dx, index
     mov     ax, es:[si+1]
     out     dx, ax
     jmp     SR_set_reg

SR_Indexed_ORed:
     ; Get the address of the index register and OR the data to it.
     add     dx, index
     mov     al, es:[si+1]
     out     dx, al
     in     ax, dx
     or     ah, es:[si+2]
     out     dx, ax
     jmp     SR_set_reg

SR_Indexed_ANDed:
     ; Get the address of the index register and AND the data to it.
     add     dx, index
     mov     al, es:[si+1]
     out     dx, al
     in     ax, dx
     and     ah, es:[si+2]
     out     dx, ax
     jmp     SR_set_reg

SR_set_reg:

     ; Adjust si to point to the next triplet and loop back.
     add     si, 3
     jmp     SR_set_regs_loop

SR_set_regs_finished:

     ret

SetRegisters ENDP

;*******************************************************************************
; Function: SetMode
;
; expects a pointer to a MODEDATA structure. It sets the current
; XGA instance into the (native) mode specified by the DMQS data within
; the structure.
;*******************************************************************************
PUBLIC SetMode
SetMode PROC NEAR

     ; We will use bp as a general register inside this function.
     push     bp

     ; Get the pointer to the start of the mode info data passed to us.
     les     bx, es:[bx].GIODataPack

     ; Find out the width (in pels) required.
     mov     ax, es:[bx].usPelsWide

     ; Find out the bits per pel required.
     mov     dl, es:[bx].bBitsPerPel

     ; Get the pointer to the mode data register triplets and store it
     ; on the stack for later.
     les     bx, es:[bx].pDMQSModeData
     push     es
     push     bx


     ; The mode is set up in three stages. Stage one and three are common
     ; to all modes and only need adjusting for bits per pel. Stage two
     ; uses the mode data passed to us from the BVH. For the bit per pel
     ; adjustment for stages one and three we must now convert the bits
     ; per pel to format. For the display pixel map width for stage three
     ; we will calculate the with of the bitmap as a multiple of 8 bytes.
     mov     bl, 3
     cmp     dl, 8
     je     SM_got_format
     mov     bl, 4
     shl     ax, 1
     cmp     dl, 16
     je     SM_got_format
     mov     bl, 2
     shr     ax, 2
SM_got_format:

     ; Keep the width of the screen bitmap (as a multiple of 8 bytes)
     ; for use later in the top half of ebx.
     shr     ax, 3
     rol     ebx, 16
     mov     bx, ax
     rol     ebx, 16


     ; First do the common registers we need to set before the CRTC DMQS
     ; mode data.
     push     ds
     pop     es

     ; Get offset of first byte of mode data
     mov     si, OFFSET PreModeData[1]

     ; Get offset of last byte of mode data
     movzx     cx, byte ptr PreModeData[0]
     add     cx, OFFSET PreModeData[0]

     ; Adjust the mode data according to bits per pel.
     xchg     si, cx
     mov     es:[si], bl
     xchg     si, cx

     ; Set the pre CRTC DMQS mode data.
     ; (es:si = first byte of mode data, es:cx = last byte of mode data).
     call     SetRegisters



     ; Get the back pointer to the mode data passed to us.
     pop     bp
     pop     es

     ; Get the length of the mode data (in bytes).
     mov     cx, word ptr es:[bp].usLength

     ; Convert this to the address of the last byte of data.
     add     cx, bp
     dec     cx

     ; Get the start of the mode settting register value triplets.
     mov     si, bp
     add     si, word ptr es:[bp].usModeDataOffset

     ; Call the SetRegisters function to do the actual register setting
     ; according to the mode data triplets.
     ; (es:si = first byte of mode data, es:cx = last byte of mode data).
     call     SetRegisters

     ; Now do the common registers we need to set after the CRTC DMQS
     ; mode data.
     push     ds
     pop     es
     mov     si, OFFSET PostModeData[1]
     movzx     cx, byte ptr PostModeData[0]
     add     cx, OFFSET PostModeData[0]

     ; Adjust the mode data according to bits per pel and screen width.
     xchg     si, cx
     mov     es:[si-3], bl               ; Set bits per pel.
     rol     ebx, 16                ; Get width (8*bytes) into bx.
     mov     es:[si-9], bl               ; Set DPM width low.
     mov     es:[si-6], bh               ; Set DPM width hi.
     xchg     si, cx

     ; Set the pre CRTC DMQS mode data.
     ; (es:si = first byte of mode data, es:cx = last byte of mode data).
     call     SetRegisters

     ; We are leaving palette loading up to the caller. If it is ever
     ; made part of this function, this is where it happens.


     ; clear out the entire VRAM to prevent any garbage appearing
     mov     eax, instances[0].XGA_base_vram
     mov     ecx, instances[0].XGA_vram_size
     movxga     instances.XGA_mem_map_virt

     memregwrite     hw_pi_map_index, SEL_PIX_MAP_B
     memregwrite     hw_pi_map_base_ptr, eax

     ; Set hardware MapB Width = dimension 1 = 1024
     mov     ax, 3ffh
     memregwrite     hw_pi_map_width, ax
     memregwrite     dim1, ax


     ; Set hardware MapB Height = dimension 2 = VRAM size / 1024
     shr     ecx, 0ah
     dec     cx
     memregwrite     hw_pi_map_height, cx
     memregwrite     dim2, cx

     ; Set hardware MapB Format = 8bpp
     memregwrite     hw_pi_map_format, EIGHT_BPP

     ; Set hardware Foreground colour = 0's
     memregwrite     fg_colour, 0h

     ; Set hardware Foreground mix = overpaint
     memregwrite     fg_mix, HWMIX_SOURCE

     ; Set hardware Destination map x/y = 0
     memregwrite     dest_map, 0

     ; Ensure the pixel mask has all bits enabled for update
     memregwrite     plane_mask, 0ffffh

     ; Disable destination update inhibition (ie. allow updates)
     memregwrite     colour_comp_fun, 04h

     ; Set hardware Pixel Operation to:-
     ;  Bg source = xxxx
     ;  Fg source = Fg register
     ;  Step      = PxBlt
     ;  Source    = xxxx
     ;  Dest      = MapB
     ;  Patt      = Fg fixed
     ;  Mask      = disabled
     ;  Draw mode = xxxx
     ;  Oct          = 000
     memregwrite     pixel_op, solid_blt_b

; this could be a waitlong!
     waitshort

SM_blt_complete:

     ; Now set the border colour to its initial value and set all
     ; bits in the palette mask.
     mov     dx, instances[0].XGA_io_base
     add     dx, index

     mov     al, border_colour
     xor     ah, ah
     out     dx, ax

     mov     al, palette_mask
     mov     ah, 0ffh
     out     dx, ax

     sub     dx, index          ; restore the base pointer

; make sure the pc_vram window is zero
     add     dx, pc_vram_indx
     xor     al,al
     out     dx,al
     sub     dx, pc_vram_indx

SM_exit:
     mov     ax, TRUE
     pop     bp
     ret

SetMode ENDP

;*******************************************************************************
; Function: SetModeInfo
;*******************************************************************************
PUBLIC SetModeInfo
SetModeInfo PROC NEAR

     ; Keep a copy of the pointer to the mode information.
     mov     eax, es:[bx].GIODataPack
     mov     pBVHModeDataInfo, eax

     mov     ax, TRUE
     ret

SetModeInfo ENDP

;*******************************************************************************
; Function: GetModeInfo
;*******************************************************************************
PUBLIC GetModeInfo
GetModeInfo PROC NEAR

     ; Get the far pointer to the start of the mode info the caller
     ; wants us to fill in.
     les     di, es:[bx].GIODataPack

     ; Save our data segment.
     push     ds

     ; Get a far pointer to the BVHs mode data.
     lds     si, pBVHModeDataInfo

     ; Work out the number of bytes to copy.
     mov     al, ds:[si].bNumberOfModes
     mov     cl, size MODEDATA
     mul     cl
     mov     cx, ax               ; size of ModeData array field.
     add     cx, size bNumberOfModes+size primary_adapter_slot

     ; Copy the data.
     rep movs byte ptr es:[di], byte ptr ds:[si]

     ; Get our data segment back.
     pop     ds

     ; Return OK to the caller.
     mov     ax, TRUE
     ret

GetModeInfo ENDP

;*******************************************************************************
; Function: GetPosData
;
; fills in the POS id and data fields in the XGA instance
; structure. This information is found using the slot number which must
; already be set up in the instance field. A slot number of zero indicates
; that the planar device should be querried. If the device at the specified
; slot is not an XGA then the POS id field is not updated.
;*******************************************************************************
PUBLIC GetPosData
GetPosData PROC NEAR

; First we clear the POS id so that if we do not manage to find an XGA
; then the calling routine will get this null value and can act accordingly.
     mov     instances[0].pos_id, 0

; Now we want to set things up so that we are ready to read
; in the POS data from whichever slot we are using.

; Get Logical ID request block for POS data
     mov     al, 010H               ;device id (POS regs)
     mov     bl, 0                    ;get first free LID
     mov     dh, 1                    ;dma, pos
     mov     dl, DevHlp_GetLidEntry          ;call OS2
     call     Device_Help

     jc     GPD_Exit                 ; no carry means no error

; Set Read POS data in Logical ID request block
     mov     si, OFFSET RequestBlock      ;Request block in our DSEG
     mov     RequestBlock.LID, ax               ;(LID from OS2 call)
     mov     RequestBlock.Unit, 0               ;unit zero (no units)
     mov     RequestBlock.Data_Sel, ds
     mov     RequestBlock.Function, 0Bh          ;function 'read pos data'
     mov     RequestBlock.ReqBlockLen, TYPE Request_Block

; Now get the information for the slot requested. If the slot number is zero
; then we get the information from the planar.
     mov     cl, instances[0].slot_number
     or     cl, cl
     jnz     check_slot

; Check the planar for an XGA - need to do this by going direct to the h/w.
; Currently the ABIOS call to read the planar POS returns the Planar ID
; rather than the ID etc. of the video adapter.
     mov     dx,0100h     ; Start of POS.
     cli               ; Prevent interrupts from occuring whilst
                    ; XGA on planar is in setup mode.
     mov     al,0dfh
     out     94h,al          ; XGA is now in setup mode and POS can
                    ; be read or written as if it was in a slot.
     in     ax,dx          ; Get POS ID
     cmp     ax,XGA_ID_LOWEST; Check for XGA ID
     jl     no_planar_XGA
     cmp     ax,XGA_ID_HIGHEST
     jg     no_planar_XGA

; XGA present so read the POS and store in the XGAINSTANCE.
     mov     instances[0].pos_id, ax      ; Store in XGAINSTANCE pos id
     add     dx,2
     in     eax,dx                    ; Get POS 102h to 105h
     mov     dword ptr instances[0].pos_data, eax     ; Store in XGAINSTANCE pos data area

no_planar_XGA:
     mov     al,0ffh
     out     094h,al      ; Take XGA on planar out of setup mode.
     sti               ; Enable interrupts again.

     jmp     GPD_exit


check_slot:

; Set up the request block for this call
     mov     RequestBlock.SlotNo, cl           ; set ABIOS slot number
     mov     RequestBlock.ReturnCode, 0FFFFh      ; reset ABIOS returnCode

; Set pointer to data buffer for POS data = address of pos_data
     lea     ax, instances[0].pos_data
     mov     RequestBlock.Data_Off, ax          ; returned pos data area

; Call ABIOS
     mov     dh, 0                    ;ABIOS entry point 'Start'
     mov     dl, DevHlp_ABIOSCommonEntry     ;ABIOS call
     call     Device_Help

; Check for error conditions.
     jc     GPD_exit                 ; no carry or non zero return
     cmp     RequestBlock.ReturnCode, 0          ; code means error has oocured
     jne     GPD_exit                 ; so just exit.

; Pick up the read pos ID
     mov     ax, RequestBlock.adaptorID

     cmp     ax,XGA_ID_LOWEST; Check for XGA ID
     jl     GPD_exit
     cmp     ax,XGA_ID_HIGHEST
     jg     GPD_exit                 ; skipp if not

slot_is_XGA:
     mov     instances[0].pos_id,ax


GPD_exit:

; Free Logical ID request block
     mov     ax, requestBlock.LID          ;get LID for OS2
     mov     dl, DevHlp_FreeLIDEntry      ;free up our LID
     call     Device_Help               ;do it

     ret

GetPosData ENDP

;*******************************************************************************
;* Function: DecodePOS
;*
;* Purpose:  One time code to set up Reg Bases and VRAM start
;*******************************************************************************
PUBLIC DecodePOS
DecodePOS PROC NEAR

     pusha

; Decode the XGA pos registers
; Get I/O base address from POS register 2
     mov     al, instances[0].pos_data+0     ;get POS Reg 2 (Byte 1)
     and     al, 0eh           ;remove unwanted bits
     shl     al, 3               ;move to top nibble
     mov     ah, 21h           ;bx now has I/O base address
                         ; I/O 000b = 2100h
                         ;     001b = 2110h  etc

; Save i/o base
     mov     instances[0].XGA_io_base, ax

; Get Mem Map base address from POS register 2
;      Co-processor ROS Addr 0000b = C1C80h
;                      0001b = C3C80h  etc
     mov     ebx, 0c1c00h          ;set Mem map base address     ;**002
     xor     eax, eax
     mov     al, instances[0].pos_data+0     ;get POS Reg 2 (Byte 1)
     and     al,00eh           ;remove unwanted bits          ;**002
     shl     eax,6               ;move to correct place          ;**002
     add     ebx,eax           ;add low offset (IODA)          ;**002
     xor     eax,eax           ;                    ;**002
     mov     al, instances[0].pos_data+0     ;get POS Reg 2 (Byte 1)      ;**002
     and     al,0f0h           ;remove unwanted bits          ;**002
     shl     eax,9               ;move to correct place          ;**002
     add     ebx,eax           ;add high offset (ROS)          ;**002

; Save XGA_mem_map_phys
     mov     instances[0].XGA_mem_map_phys, ebx

; Read from POS byte 3 and 4 address the address of VRAM in PC space
     xor     eax, eax
     mov     bl, instances[0].pos_data+0     ;get POS Reg 2 (Byte 1)
     and     bl,0eh               ;remove unwanted bits
     shr     bl,1               ;shift to correct place
     mov     al, instances[0].pos_data+2     ;get POS Reg 4 (Byte 3)
     and     al,0feh           ;remove unwanted bit
     shl     ax,2               ;shift to correct place
     or     al,bl               ;or in IODA
     shl     eax, 22           ;eax was 4M units, but we want bytes

; Save in XGA_base_vram
     mov     instances[0].XGA_base_vram, eax

; Find the 1meg aperture
     mov     al, instances[0].pos_data+3     ;get POS Reg 5 (Byte 4)
     and     eax, 0fh          ; keep bits 0 to 3
     shl     eax, 20           ; convert to physical address
     mov     instances[0].XGA_1M_aperture_phys, eax

; Find the 4meg aperture
     xor     eax,eax           ; assume aperture does not exist
     test     instances[0].pos_data[2], 1     ; is the 4M enabled
     jz     @f               ; jump if not present

     ; if present the aperture is the same as the VRAM base address
     mov     eax, instances[0].XGA_base_vram
@@:
     mov     instances[0].XGA_4M_aperture_phys, eax


map_into_flat_space:

; Now do the physical to linear (ie. flat) conversions

; Map the XGA registers into FLAT address space
     mov     eax, 00010000b          ; get linear addr for phys addr
     mov     ecx, 4096          ; page size is 4k
     mov     edi, instances[0].XGA_mem_map_phys
     and     edi, 0FFFFF000h      ; round down to page boundary
     mov     XGARegPointer, edi
     mov     edi, FlatXGARegPointer
     mov     dl,DevHlp_VMAlloc
     call     Device_Help          ; eax = linear addr

     mov     edx, instances.XGA_mem_map_phys
     and     edx, 0FFFh          ; keep offset within the page
     add     eax,edx           ; add to the page start

     mov     instances[0].XGA_mem_map_flat, eax

; Map the 1M aperture if present into FLAT address space
     mov     eax, instances[0].XGA_1M_aperture_phys
     and     eax,eax
     jz     no_1M_aperture

     ; get a linear address for the physical aperture
     mov     XGARegPointer, eax     ; put the physical address in place
     mov     eax, 00010000b          ; get linear addr for phys addr
     mov     ecx, 100000h          ; aperture is 1M long
     mov     edi, FlatXGARegPointer
     mov     dl,DevHlp_VMAlloc
     call     Device_Help          ; eax = linear addr

no_1M_aperture:
     mov     instances[0].XGA_1M_aperture_flat, eax

; Map the 4M aperture if present into FLAT address space
     mov     eax, instances[0].XGA_4M_aperture_phys
     and     eax,eax
     jz     no_4M_aperture

     ; get a linear address for the physical aperture
     mov     XGARegPointer, eax     ; put the physical address in place
     mov     eax, 00010000b          ; get linear addr for phys addr
     mov     ecx, 100000h          ; aperture is 1M long
     mov     edi, FlatXGARegPointer
     mov     dl,DevHlp_VMAlloc
     call     Device_Help          ; eax = linear addr

no_4M_aperture:
     mov     instances[0].XGA_4M_aperture_flat, eax

pos_decoded:
     clc                    ;no errors ...
     popa
     ret

DecodePOS ENDP

;*******************************************************************************
; Function: TestPrimaryAdapter
;
; Purpose:  To determine the primary adapter flag
;*******************************************************************************
TestPrimaryAdapter PROC NEAR

; assume this is not the primary adapter
     mov     instances[0].primary_adapter, false

     push     dx
     push     ax

     mov     dx, instances[0].XGA_io_base

; With the XGA we assume that all cards start in native mode.
; If the card is no longer in native mode than it must have been selected
; as the primary adapter.
     ioread     al, op_mode
     test     al, MODE_EXT_GRAPHICS
     jnz     not_vga_mode

; Set primary_adapter = true
     mov     instances[0].primary_adapter, true
     jmp     short not_vga

not_vga_mode:

; We blank the screen of any non primary XGA cards
; Set hardware display mask = 0's
     mov     ah,0
     mov     al,palette_mask
     iowrite index,ax

not_vga:

     pop     ax
     pop     dx
TPA_exit:
     ret

TestPrimaryAdapter  ENDP

IODELAY macro
     jmp     $+2
     jmp     $+2
     jmp     $+2
endm

;*******************************************************************************
; Function: GetMonitorID
;
; Code based very closely on code provided by Peter Monnes
;*******************************************************************************
PUBLIC GetMonitorID
GetMonitorID PROC NEAR

;  Reset CRTC, blank

     push     bx
     push     cx
     push     dx
     push     di
     push     si

     ; Get the base address of the XGA registers into dx.
     mov     si, instances[0].XGA_io_base
     add     si,index
     mov     dx,si               ; Point to index
     mov     ax,disp_mode1
     out     dx,al
     inc     dx
     in     al,dx
     mov     savemode1,al          ; save disp_mode_1
     and     al,0FDh
     out     dx,al               ; prepare for reset
     in     al,dx
     and     al,03Eh           ; blank screen and reset, leave v,H
     or     al,40h               ; SP=01 makes v ground, h voltage
     out     dx,al               ; 

;Read bits
;
;  For x = 1 to 4
;    set different v,h combo
;    read id(x)
;    store 4 bits per id read
;  end

     xor     ax,ax               ; save 0 in di for later (compid calc)
     mov     di,ax
     lea     bx, settings          ; point to settings
     mov     cx,4
get4bits:
     mov     dx,si
     mov     al,disp_mode1
     out     dx,al               ; Select index disp_mode1
     inc     dx
     in     al,dx               ; read index disp_mode1
     xor     al,[bx]           ; flip v or h
     inc     bx               ; point to next setting
     out     dx,al               ; Set v and h according to 'settings'
     dec     dx
     mov     al,monitor_id
     out     dx,al               ; select ID register
     inc     dx
     in     al,dx               ; read ID
     and     ax,0Fh               ; mask desired id bits
     shl     di,4               ; shift to make room for more bits
     add     di,ax               ; add new bits read to it
     loop     get4bits
                         ; 4 reads (1 word) left on stack
; Restore disp_mode1 to original value

     mov     dx,si
     mov     al,disp_mode1
     out     dx,al               ; Select index disp_mod1
     inc     dx
     mov     al,savemode1
     out     dx,al               ; read index disp_mode1

;Rearrange the bits to get the composite monitor id
     push     di          ; put read result on stack for later use
     xor     ax,ax
     mov     di,ax          ; use di to hold result of reorganization
     mov     cx,16          ; 16 bits to rearrange
rearrangebits:
     mov     ax,1
     dec     cx          ; CX goes 16 to 1, bit pos go 15 to 0
     shl     ax,cl          ; shift 1 by cx times
     inc     cx          ; restore cx
     pop     bx          ; get id read
     push     bx
     and     bx,ax          ; check CXth bit
     je     nextbit
     lea     bx, adders-1     ; CXth bit is a one
                    ; adjust since cx goes to 1 not 0
     add     bx,cx          ; pt to correct adder
     push     cx
     xor     cx,cx
     mov     cl,[bx]      ; get amount to shift from adders array
     mov     ax,1
     shl     ax,cl          ; shift bit to correct position
     pop     cx
     add     di,ax          ; add bit to result
nextbit:
     loop     rearrangebits

     ; di now contains the adjusted composite monitor ID.
     mov     word ptr instances[0].XGA_monitor_id, di

     pop     bx          ; bits read are lingering on stack, remove

     pop     si
     pop     di
     pop     dx
     pop     cx
     pop     bx
     ret

GetMonitorID  ENDP

;*******************************************************************************
; Function: GetHWLevel
;
; Purpose:  To determine the level of the hardware
;*******************************************************************************
PUBLIC GetHWLevel
GetHWLevel PROC NEAR

     push     dx
     push     ax

     mov     dx, instances[0].XGA_io_base

     ; Get system id into XGAINSTANCE

     mov     al, system_id
     iowrite index, al
     ioread     al, dataio

     ; Get the base XGA implementation level into al and the SPD level
     ; into ah.
     movzx     ax, al
     shl     ax, 4
     shr     al, 4
     xchg     ah, al
     mov     word ptr instances[0].XGA_level, ax

     pop     ax
     pop     dx
GHWL_exit:
     ret

GetHWLevel  ENDP

;*******************************************************************************
; Function:  GetVRAMSize
;
; Purpose:   To determine the amount of memory installed
;*******************************************************************************
GetVRAMSize PROC NEAR

     cli
     push          bx
     push          cx
     push          edx
     push          esi
     pushxga

     mov          dx, instances[0].XGA_io_base            ;ioregbase

; Set hardware interrupt enable register = 0
     mov          al, 0
     iowrite      int_enable, al

; Set intel bit in op_mode
     ioread          al, op_mode
     and          al, not MOTOROLA
     iowrite      op_mode, al

; we have done our last io usage, so edx is now free

     movxga          instances[0].XGA_mem_map_virt        ;memregbase
     waitshort

; Set hardware carry chain mask to allow all 8 bits to be affected
     mov          ax, 7fh
     memregwrite     carry_chain_mask, ax

; Set hardware MapA Format = 8 bits/pixel
     memregwrite     hw_pi_map_index, SEL_PIX_MAP_A
     memregwrite     hw_pi_map_format, EIGHT_BPP

; Set hardware MapA Height and width
     memregwrite     hw_pi_map_height, 0
     memregwrite     hw_pi_map_width, 0

; Set hardware operation dimensions
     memregwrite     dim1, 0
     memregwrite     dim2, 0

; Set hardware update enable = ffffh
     memregwrite     plane_mask,0ffffh

; Set hardware foreground mix to overpaint
     memregwrite     fg_mix, HWMIX_SOURCE

; Set hardware colour compare to false (inhibit)
     memregwrite     colour_comp_fun, 4

; Calculate physical address of XGA_vram_size
     mov          eax, instances[0].phys_addr_instance
     mov          bx, OFFSET XGA_vram_size
     movzx          ebx, bx
     add          eax, ebx

; Set hardware MapB Base pointer = address of XGA_vram_size
     memregwrite     hw_pi_map_index, SEL_PIX_MAP_B
     memregwrite     hw_pi_map_base_ptr, eax

; Set hardware MapB Format = 8 bits/pixel
     memregwrite     hw_pi_map_format, EIGHT_BPP

; Set hardware MapB Height and width
     memregwrite     hw_pi_map_height, 0
     memregwrite     hw_pi_map_width, 0

; (XGA always takes 4M of memory space)
; (and is assumed to always have at least 256K)
; Set pointer = start of VRAM + 512K - 1
     mov          edx, 512*1024
     add          edx, instances[0].XGA_base_vram
     dec          edx

; Do 15 times (up to 4M of VRAM)
     mov          cx, 15
next_mem_chk:

; Set hardware MapA Base = pointer
     memregwrite     hw_pi_map_index, SEL_PIX_MAP_A
     memregwrite     hw_pi_map_base_ptr, edx

; Set hardware source Map X, Y = 0
     memregwrite     src_map, 0

; Set hardware dest Map X, Y = 0
     memregwrite     dest_map, 0

; (Blt original pixel from VRAM)
; Set hardware Pixel Operations Register to:
;     Bg source = Background colour
;     Fg source = Foreground colour
;     Step       = PxBlt
;     Source       = MapA
;     Dest       = MapB
;     Patt       = xxxx
;     Mask       = disabled
;     Draw mode = xxxx
;     Oct       = 000 (+x+y)
     memregwrite     pixel_op,thru_blt_b
     waitshort

; Save original pixel value in temp variable
     mov          bx, word ptr instances[0].XGA_vram_size

; Set hardware dest Map X, Y = 0
     memregwrite     dest_map, 0

; Set hardware foreground colour register = 55h
     memregwrite     fg_colour, 055h

; (Blt pixel to VRAM)
; Set hardware Pixel Operations Register to:
; Fg source = Foreground colour
;     Bg source = xxxx
;     Step       = PxBlt
;     Source       = xxxx
;     Dest       = MapA
;     Patt       = Foreground Fixed
;     Mask       = disabled
;     Draw mode = xxxx
;     Oct       = 000 (+x+y)
     memregwrite     pixel_op,solid_blt
     waitshort

; Set hardware source Map X = 0
; Set hardware source Map Y = 0
     memregwrite     src_map, 0

; Set hardware dest Map X = 0
; Set hardware dest Map Y = 0
     memregwrite     dest_map, 0

; (Blt pixel from VRAM)
; Set hardware Pixel Operations Register to:
;     Bg source = Background colour
;     Fg source = Foreground colour
;     Step       = PxBlt
;     Source       = MapA
;     Dest       = MapB
;     Patt       = xxxx
;     Mask       = disabled
;     Draw mode = xxxx
;     Oct       = 000 (+x+y)
     memregwrite     pixel_op,thru_blt_b
     waitshort

; If data != 55h, break
     cmp          instances[0].XGA_vram_size, 055h
     je          mem_tst_ok
     jmp          mem_tst_fail

mem_tst_ok:

; Set hardware dest Map X, Y = 0
     memregwrite     dest_map, 0

; Set hardware foreground colour register = AAh
     memregwrite     fg_colour, 0AAh

; (Blt pixel to VRAM)
; Set hardware Pixel Operations Register to:
;     Fg source = Foreground colour
;     Bg source = xxxx
;     Step       = PxBlt
;     Source       = xxxx
;     Dest       = MapA
;     Patt       = Foreground Fixed
;     Mask       = disabled
;     Draw mode = xxxx
;     Oct       = 000 (+x+y)
     memregwrite     pixel_op,solid_blt
     waitshort

; Set hardware source Map X, Y = 0
     memregwrite     src_map, 0

; Set hardware dest Map X, Y = 0
     memregwrite     dest_map, 0

; (Blt pixel from VRAM)
; Set hardware Pixel Operations Register to:
;     Bg source = Background colour
;     Fg source = Foreground colour
;     Step       = PxBlt
;     Source       = MapA
;     Dest       = MapB
;     Patt       = xxxx
;     Mask       = disabled
;     Draw mode = xxxx
;     Oct       = 000 (+x+y)
     memregwrite     pixel_op,thru_blt_b
     waitshort

; If data != AAh, break
     cmp          instances[0].XGA_vram_size, 0AAh
     jne          mem_tst_fail

; Set hardware dest Map X, Y = 0
     memregwrite     dest_map, 0

; Set hardware foreground colour register = original pixel value
     memregwrite     fg_colour, bx

; (Blt original pixel to VRAM)
; Set hardware Pixel Operations Register to:
;     Fg source = Foreground colour
;     Bg source = xxxx
;     Step       = PxBlt
;     Source       = xxxx
;     Dest       = MapA
;     Patt       = Foreground Fixed
;     Mask       = disabled
;     Draw mode = xxxx
;     Oct       = 000 (+x+y)
     memregwrite     pixel_op,solid_blt
     waitshort

; Add 256K to pointer
     add          edx, 256*1024
     dec          cx
     jcxz          mem_tst_fail
     jmp          next_mem_chk

mem_tst_fail:

; VRAM installed = pointer + 1 - 256K

     inc          edx
     sub          edx, 256*1024
     sub          edx, instances[0].XGA_base_vram

; If command line vram size override
     cmp          vRamSpace, 0
     je          vSize

; Set VRAM installed = command line value * 1024
     movzx          edx, vRamSpace
     imul          edx, 1024
vSize:
     mov          instances[0].XGA_vram_size, edx

     popxga
     pop          esi
     pop          edx
     pop          cx
     pop          bx

     sti
GVS_exit:
     ret

GetVRAMSize ENDP

;*******************************************************************************
; Function: GetInstanceGDT
;*******************************************************************************
GetInstanceGDT PROC NEAR

     push     ecx
     push     edi
     push     esi

; Get 2 GDT selectors for the XGA instance we are using (one for the instance
; data itself and one for the field XGA_mem_map_virt within the instance data).
     push     ds
     pop     es                    ; es = selector of GDT.
     mov     di, OFFSET GDTselectors      ; di = offset of GDT.
     mov     cx, 2                    ; cx = no of selectors
     mov     dl, DevHlp_AllocGDTSelector
     call     Device_Help
     jc     GIG_exit               ; If error then exit.

; Get the physical address of our XGA instance data.

; Get XGAINSTANCE physical address from virtual: ds:si = virtual address
     mov     si, OFFSET instances
     mov     dl, DevHlp_VirtToPhys
     call     Device_Help
     jc     GIG_exit               ; If error then exit.

; Use PhysToGDTSel to set the access rights for our GDT selector.

;***EP     PhysToGDTSel - store physical address and access in GDT selector
;
;     Converts a physical memory address to a designated GDT selector,
;     setting the access byte of the descriptor to desired privilege value.
;     The indicated segment must be a fixed block of memory or locked
;     (either long or short term) before issuing this request.
;
;     This routine can be called in interrupt time. It must not
;     block, it must not get any faults.
;
;     Once this call has been issued for a particular selector,
;     that addressability will remain valid until the device driver
;     changes its content via a subsequent dh_PhysToGDTSel request
;     referencing the same GDT selector.  This call can be made in
;     protected mode only.
;
;     ENTRY     (EAX)     = Address - 32 bit physical address
;          (ECX)     = Size - the 32 bit segment size, not the limit
;                 Converted to a limit before editing descriptors
;                 Must be <= 64Kb
;          (DH)     = Descriptor privilege
;                 0: Make segment readable code (286 format)
;                 1: Make segment writable data (286 format)
;                 3: Make segment readable IOPL code (286 format)
;                 4: Make segment writable IOPL data (286 format)
;                 5: Make segment readable Ring 0 code (286 format)
;                 6: Make segment writable. Ring 0 data (286 format)
;               128: Add to any of the above to get access privileges
;                   in the 386 chip format (32 bit addressing)
;          (SI)     = Selector - for the descriptor to be setup
;     EXIT     Carry Flag clear on success
;          (SI)     = Selector with the right RPL stamp
;          Carry Flag set on failure
;              (AX) = ERROR_INVALID_ADDRESS
;                  ERROR_INVALID_SELECTOR
;                  ERROR_INVALID_PARAMETER
;     USES     EAX, ESI, Flags


; Save physical address of XGAINSTANCE in phys_addr_instance (the instance itself)
     shl     eax, 10h
     mov     ax, bx
     mov     dword ptr [si].phys_addr_instance, eax

     ; map the physical address to one of our GDT selectors
     ; eax = physical address
     mov     si, GDTselectors[0]     ; get selector to map to
     mov     ecx, SIZEOFXGAINSTANCE     ; Size
     mov     dh, 1               ; Access rights: ring3 writable
     mov     dl, DevHlp_PhysToGDTSel
     call     Device_Help          ; do it : si = modified selector
     jc     GIG_exit          ; quick exit if an error

; ?? We do not seem to use this GDT we have just got (ie modified si) anymore
; so we should probably take the above out of the code.

     ; Now the memory mapped registers
     mov     eax, instances[0].XGA_mem_map_phys
     mov     ecx, MEMREGBLKLEN     ; Size of the XGA registers
GIG_got_selector_length:
     mov     si, GDTselectors+2     ; Get selector to map to
     mov     dh, 1               ; Access rights: ring3 writable
     mov     dl, DevHlp_PhysToGDTSel
     call     Device_Help          ; do it : si = modified selector
     jc     GIG_exit          ; quick exit if an error

     ; save the virtual address in the instance
     shl     esi, 10h
     mov     dword ptr instances[0].XGA_mem_map_virt, esi


     clc                              ;'no error' flag
GIG_exit:
     pop     esi
     pop     edi
     pop     ecx
     ret

GetInstanceGDT  ENDP

;*******************************************************************************
; Function: FillInInstanceData
;
; is called when the BVH passes down null instance
; data for the adapter it wants the ring0 to use. This means that the
; BVH does not have the required information so we must work it out here.
; We first try to find the DMQS primary data (in the Extended BIOS Data Area)
; for the slot. If we can not find this then we must use the POS data and id
; to try to work out the instance data.
; The BVH has set up the slot number and we must now try to fill up the rest.
; Note that there may not even be an XGA adapter at the requested slot,
; in which case we will not set any of the instance data.
;*******************************************************************************
PUBLIC FIllInInstanceData
FillInInstanceData PROC NEAR

; First we want to look for the DMQS primary data for this slot. If no DMQS
; XGA data is found for this slot then the pos id returned is null.
     call     GetDMQSPrimaryData
     cmp     instances[0].pos_id, 0
     je     FIID_useposdata

; Call MAPGDT to generate the GDT entries for this XGA instance.
     call     GetInstanceGDT
     jmp     FIID_gotdata

FIID_useposdata:
; We must try to fake all the DMQS primary data by using the POS data and ID.

; First we want to get the POS data and id for the requested slot. If no XGA
; is found in the slot then the id returned is null.
     call     GetPosData
     cmp     instances[0].pos_id, 0
     je     FIID_exit

; Call DecodePOS to set up Reg Bases, VRAM start, and appature info.
     call     DecodePOS
     jc     FIID_error               ; if error ???

     call     GetMonitorID               ; get monitor id

     call     GetHWLevel               ; get hardware level

; Call MAPGDT to generate the GDT entries for this XGA instance.
     call     GetInstanceGDT               ; map GDT entries
ifdef FIREWALLS
     jnc     @F                    ; if no error
     int     3
@@:
endif ; FIREWALLS

; Now call GetVRAMSize - note that GetInstanceGDT must already have been called.
     call     GetVRAMSize               ; get memory size

FIID_gotdata:

; We must check to see if we are the primary adapter (even if DMQS primary
; data was found for this slot) because DMQS can not supply this information.
     call     TestPrimaryAdapter


FIID_exit:
     ret

FIID_error:
     int 3
     ret

FillInInstanceData  ENDP

;*******************************************************************************
; Function: SetInstanceData
;*******************************************************************************
PUBLIC SetInstanceData
SetInstanceData PROC NEAR

     ; Get the pointer to the instance data passed to us.
     les     si, es:[bx].GIODataPack

     ; Get a pointer to our local instance data.
     mov     di, OFFSET instances[0]

     ; Get the number of bytes to copy.
     mov     cx, SIZEOFXGAINSTANCE

     ; Swap ds and es.
     push     ds
     push     es
     pop     ds
     pop     es

     ; Copy the data to our local instance data.
     rep movs byte ptr es:[di], byte ptr ds:[si]

     ; Restore ds.
     push     es
     pop     ds

     ; If the instance data is not complete then try to complete it.
     ; We look at the XGA level identifier to see if the data is complete.
     ; If on return it is still zero then there is no XGA at this slot.
     cmp     instances[0].XGA_level, 0
     jne     SID_DataOK

     call     FillInInstanceData

SID_DataOK:

     mov     ax, TRUE
     ret

SetInstanceData ENDP

;*******************************************************************************
; Function: GetInstanceData
;*******************************************************************************
PUBLIC GetInstanceData
GetInstanceData PROC NEAR

     ; Get the pointer to the instance data for us to set.
     les     di, es:[bx].GIODataPack

     ; Get a pointer to our local instance data.
     mov     si, OFFSET instances[0]

     ; Get the number of bytes to copy.
     mov     cx, SIZEOFXGAINSTANCE

     ; Copy the data to our local instance data.
rep  movs    byte ptr es:[di], byte ptr ds:[si]

     mov     ax, TRUE
     ret

GetInstanceData ENDP

;KJE - <DCR53004>
;*******************************************************************************
; Function: CopyDMQSData
;*******************************************************************************
PUBLIC CopyDMQSData
CopyDMQSData PROC NEAR

     push    es
     push    di

     ;**************************************************************************
     ; Ensure that the pointer to the DMQS data area in the kernel has been
     ; set properly by the OS/2 Kernel process.
     ;**************************************************************************
     cmp     fInt10DMQSData,0
     je      CopyDMQSData_ErrorExit
;    cmp     pResult, 0
;    je      CopyDMQSData_ErrorExit

     ;**************************************************************************
     ; Save current register state for re-use later on in this function.
     ;**************************************************************************
     push    es
     push    di
     push    bx

     les     di, DWORD PTR pResult
     mov     bx, 1

CopyDMQSData_GetNextDMQS:

     mov     ax, es:[di].usNextDMQS

     cmp     ax, 0
     je      CopyDMQSData_AllDMQSCounted

     inc     bx
     add     di, ax                    ; Add the offset to point to the next
                                       ;  DMQS data structure.

     jmp     CopyDMQSData_GetNextDMQS

CopyDMQSData_AllDMQSCounted:

     ;**************************************************************************
     ; Now that we have the number of DMQS data structures, calculate the
     ; number of bytes that we have to copy from the DMQS data array into the
     ; callers buffer area.
     ; 
     ; NOTE: It is assumed that the caller will ALWAYS provide a buffer area
     ;       that will be large enough to accomodate the maximum number of
     ;       XGA instances (8 currently).
     ;**************************************************************************
     mov     ax, SIZE DMQS_Int10Pdi    ; Define found in XGAADAPT.INC...
     mul     bx

     ;**************************************************************************
     ; Set up registers for REP MOV instruction below.
     ;**************************************************************************
     mov     cx, ax                    ; Get results of above MULtiply.
     pop     bx                        ; Restore from above push.
     pop     di                        ; Ibidium...
     pop     es                        ; Ibidium...

     les     di, es:[bx].GIODataPack   ; Setup destination address; the caller's
                                       ;  address to the DMQS data buffer.

     lds     si, pResult               ; Setup source address; the address of
                                       ;  the DMQS data buffer area kept by
                                       ;  the OS/2 Kernel.

     ;**************************************************************************
     ; Now, copy the DMQS data to the callers buffer area.
     ;**************************************************************************
rep  movs    BYTE PTR es:[di], BYTE PTR ds:[si]

     mov     ax, TRUE
     jmp     CopyDMQSData_Exit

CopyDMQSData_ErrorExit:

     mov     ax, FALSE
     jmp     CopyDMQSData_Exit

CopyDMQSData_Exit:

     ;**************************************************************************
     ; Restore registers that were saved away on initial entry into this
     ; function.
     ;**************************************************************************
     pop     di
     pop     es
     ret

CopyDMQSData ENDP

;*******************************************************************************
; Function: save_coprocessor_state IOCtl request #b
;
; Purpose:  saves the coprocessor state in a user supplied buffer
;*******************************************************************************
PUBLIC save_coprocessor_state
save_coprocessor_state PROC NEAR

     push     fs
     push     gs
     push     edi
     push     esi
     push     ecx
     push     ds

     xor     edi, edi

     ; get the pointer to our buffer
     lds     si, es:[bx].GIODataPack

     ; get addressability to the hardware info
     mov     ax, SEG instances
     mov     es, ax
     mov     di, OFFSET instances

     ; get the memory mapped registers base address
     lfs     bx, dword ptr es:[di].XGA_mem_map_virt

     ; suspend the coprocessor and set it ready for saving
     or     byte ptr fs:[bx].pi_control, SUSPEND_OPERATION

@@:     ; wait until the coprocessor is suspended
     test     byte ptr fs:[bx].pi_control, OPERATION_SUSPENDED
     jz     @B
     or     byte ptr fs:[bx].pi_control, SAVE_STATE

     ; get the length of save data A (dwords)
     xor     ecx, ecx
     mov     cl, byte ptr fs:[bx].state_a

     ; get the length of save data B
     mov     dl, byte ptr fs:[bx].state_b
     xor     dh, dh

     ; save the state lengths in the parameter
     mov     word ptr ds:[si].state_a_length, cx
     mov     word ptr ds:[si].state_b_length, dx

     ; get access to the save/restore data ports
     mov     dx, word ptr es:[di].XGA_io_base
     add     dx, index

     ; get a pointer to the save buffer
     les     di, dword ptr ds:[si].saved_state

     ; access the port for state A
     mov     al, state_a_data
     out     dx, al

     ; save state A
     cld
     add     dx, (dataio-index)
     rep     insd

     ; access the port for state B
     sub     dx, (dataio-index)
     mov     al, state_b_data
     out     dx, al

     ; save state B
     add     dx, (dataio-index)
     mov     cx, word ptr ds:[si].state_b_length
     rep     insd

     ; reset the coprocessor
     mov     byte ptr fs:[bx].pi_control, TERMINATE_OPERATION

save_cop_exit:
     pop     ds
     pop     ecx
     pop     esi
     pop     edi
     pop     gs
     pop     fs

     mov     ax, TRUE
     ret

save_coprocessor_state  ENDP

;*******************************************************************************
; Function:    restore_coprocessor_state IOCtl request #c
;
; Purpose:     restores the coprocessor state from a user supplied buffer
;*******************************************************************************
PUBLIC restore_coprocessor_state
restore_coprocessor_state PROC NEAR

     push     fs
     push     di
     push     si

     ; get the pointer to our buffer
     lds     si, es:[bx].GIODataPack

     ; get addressability to the hardware info
     mov     ax, SEG instances
     mov     es, ax
     mov     di, OFFSET instances

     ; get access to the save/restore data ports
     mov     dx, word ptr es:[di].XGA_io_base
     add     dx, index

     ; get the memory mapped registers base address
     lfs     bx, dword ptr es:[di].XGA_mem_map_virt

     ; suspend the coprocessor
     or     byte ptr fs:[bx].pi_control, SUSPEND_OPERATION

     ; get the state A and B lengths
     mov     cx, word ptr ds:[si].state_b_length
     shl     ecx, 10h
     mov     cx, word ptr ds:[si].state_a_length

     ; get a pointer to the saved state
     lds     si, ds:[si].saved_state

     ; access the port for state A
     mov     al, state_a_data
     out     dx, al
     add     dx, (dataio-index)

     ; wait until the coprocessor is suspended
@@:     test     byte ptr fs:[bx].pi_control, OPERATION_SUSPENDED
     jz     @B

     ; set the coprocessor restore state
     and     byte ptr fs:[bx].pi_control, NOT(SAVE_STATE)

     ; restore state A
     cld
     rep     outsd

     ; get the state B length
     shr     ecx, 10h

     ; access the port for state B
     sub     dx, (dataio-index)
     mov     al, state_b_data
     out     dx, al

     ; restore state B
     add     dx, (dataio-index)
     rep     outsd

     ; reset the coprocessor
     mov     byte ptr fs:[bx].pi_control, TERMINATE_OPERATION

restore_cop_exit:
     pop     si
     pop     di
     pop     fs

     mov     ax, TRUE
     ret

restore_coprocessor_state  ENDP

;*******************************************************************************
; Function:    set_132_column_mode IOCtl request #d
;
; Purpose:     puts the coprocessor into 132 column mode
;*******************************************************************************
PUBLIC set_132_column_mode
set_132_column_mode PROC NEAR

     push     ecx

     ; get addressability to the hardware info
     mov     ax, SEG instances
     mov     es, ax
     mov     bx, OFFSET instances

     ; get the io register base: the index is written at offset 0x0a
     ; from here
     mov     dx, word ptr es:[bx].XGA_io_base
     add     dx, index
     mov     cx, dx

     ; prepare CRTC for reset
     mov     al, 050h
     out     dx, al
     add     dx, 2
     in     al, dx
     or     al, 05h
     out     dx, al
     jmp     @F
@@:     in     al, dx
     and     al, 0fdh
     or     al, 04h
     out     dx, al

     ; reset CRTC
     jmp     @F
@@:     in     al, dx
     and     al, 0fch
     or     al, 04h
     out     dx, al

     ; 132 column text mode
     sub     dx, 0ch
     mov     al, 03h
     out     dx, al

     ; 132 column clock frequency select
     mov     dx, cx
     mov     ax, 0154h
     out     dx, ax

     ; select internal 132 column clock
     mov     ax, 08070h
     out     dx, ax

     ; disable VFB
     mov     al, 050h
     out     dx, al
     add     dx, 2
     in     al, dx
     and     al, 0efh
     out     dx, al

     ; variations on VGA CRTC syncs
     mov     dx, 03d4h
     mov     al, 011h
     out     dx, al
     inc     dx
     in     al, dx
     and     al, 07fh
     out     dx, al
     dec     dx

     xor     al, al
     out     dx, al
     inc     dx
     mov     al, 0a4h
     out     dx, al
     dec     dx

     mov     al, 1
     out     dx, al
     inc     dx
     mov     al, 083h
     out     dx, al
     dec     dx

     mov     al, 2
     out     dx, al
     inc     dx
     mov     al, 084h
     out     dx, al
     dec     dx

     mov     al, 3
     out     dx, al
     inc     dx
     mov     al, 083h
     out     dx, al
     dec     dx

     mov     al, 4
     out     dx, al
     inc     dx
     mov     al, 090h
     out     dx, al
     dec     dx

     mov     al, 5
     out     dx, al
     inc     dx
     mov     al, 080h
     out     dx, al

     mov     dx, cx
     mov     ax, 0a31ah
     out     dx, ax

     mov     ax, 01bh
     out     dx, ax

     mov     dx, 03d4h
     mov     al, 013h
     out     dx, al
     inc     dx
     mov     al, 042h
     out     dx, al
     dec     dx

     ; disable vga CRTC reg update
     mov     al, 011h
     out     dx, al
     inc     dx
     in     al, dx
     or     al, 080h
     out     dx, al

     ; remove CRTC reset
     mov     dx, cx
     mov     al, 050h
     out     dx, al
     add     dx, 2
     in     al, dx
     or     al, 03h
     out     dx, al

     ; 8 bit characters
     mov     dx, 03c4h
     mov     al, 01h
     out     dx, al
     inc     dx
     in     al, dx
     or     al, 01h
     out     dx, al

     ; read to set attr ctrl flip flop
     mov     dx, 03dah
     in     al, dx

     ; set attr ctrl reg 13h to 0
     mov     dx, 03c0h
     mov     al, 13h
     out     dx, al
     xor     al, al
     out     dx, al
     mov     al, 020h
     out     dx, al

     pop     ecx

     mov     ax, TRUE
     ret

set_132_column_mode  ENDP

;*******************************************************************************
; Function:    save_io_regs IOCtl request #e
;
; Purpose:     saves the xga io registers
;*******************************************************************************
PUBLIC save_io_regs
save_io_regs PROC NEAR

     push     cx
     push     esi
     push     edi
     push     gs

     ; get the pointer to our buffer
     lgs     si, es:[bx].GIODataPack

     ; get addressability to the hardware info
     mov     ax, SEG instances
     mov     es, ax
     mov     bx, OFFSET instances

     ; get the io register base
     mov     dx, word ptr es:[bx].XGA_io_base

     ; get a pointer to the list of registers to save
     lea     bx, io_reg_numbers

     ; use di to record the number of registers read
     xor     di, di


s_next_non_ind:
     ; get the offset to the next register
     mov     al, byte ptr ds:[bx+di]
     cmp     al, 0ffh
     jz     s_ind_begin

     ; get the register address in dx and get the data
     add     dl, al
     in     al, dx
     ror     eax, 8
     inc     di
     cmp     di, 4
     jne     s_next_non_ind

     ; got a full 32 bits of data so write to the user buffer
     mov     dword ptr gs:[si], eax
     add     si, 4
     add     bx, di
     xor     di, di
     jmp     s_next_non_ind

s_ind_begin:
     ; skip past the 0ffh at end of non_indexed regs
     inc     bx

     ; get dx holding the index register address
     and     dx, 0fff0h
     add     dx, index

s_next_ind:
     ; get the next register
     mov     al, byte ptr ds:[bx+di]
     cmp     al, 0ffh
     jz     s_non_ind_done

     ; select the next register
     out     dx, al

     ; and read it
     inc     dx
     in     al, dx
     ror     eax, 8
     dec     dx
     inc     di
     cmp     di, 4
     jne     s_next_ind

s_write_ind:
     ; got a full 32 bits of data so write to the user buffer
     mov     dword ptr gs:[si], eax
     add     si, 4
     add     bx, di
     xor     di, di
     jmp     s_next_ind

s_non_ind_done:
     ; write the last data
     and     di, di
     jz     s_all_written

     ; need to shift eax right so the read data is in the least significant
     ; bytes before writing: need to shift by 8*(4-di) bits, 1 <= di <= 3
     neg     di
     add     di, 4
     mov     cl, 8
@@:     dec     di
     jz     s_do_write
     add     cl, 8
     jmp     @B
s_do_write:
     shr     eax, cl
     mov     dword ptr gs:[si], eax

s_all_written:

     pop     gs
     pop     edi
     pop     esi
     pop     cx

     mov     ax, TRUE
     ret

save_io_regs  ENDP

;*******************************************************************************
; Function:    restore_io_regs IOCtl request #f
;
; Purpose:     restores the xga io registers
;*******************************************************************************
PUBLIC restore_io_regs
restore_io_regs PROC NEAR

     push     esi
     push     edi
     push     gs
     push     cx

     ; get the pointer to our buffer
     lgs     si, es:[bx].GIODataPack

     ; get addressability to the hardware info
     mov     ax, SEG instances
     mov     es, ax
     mov     bx, OFFSET instances

     ; get the io register base
     mov     dx, word ptr es:[bx].XGA_io_base

     ; get a pointer to the list of registers to restore
     lea     bx, io_reg_numbers

     ; use di to record the number of registers written
     xor     di, di

r_more_non_ind:
     mov     eax, dword ptr gs:[si]
     add     si, 4

r_next_non_ind:
     ; get the offset to the next register
     mov     cl, byte ptr ds:[bx+di]
     cmp     cl, 0ffh
     jz     r_ind_begin

     ; get the register address in dx and get the data
     add     dl, cl
     out     dx, al
     shr     eax, 8
     inc     di
     cmp     di, 4
     jne     r_next_non_ind

     ; need another 32 bits of data from the user buffer
     add     bx, di
     xor     di, di
     jmp     r_more_non_ind

r_ind_begin:
     ; skip past the 0ffh at end of non_indexed regs
     inc     bx

     ; get dx holding the index register address
     and     dx, 0fff0h
     add     dx, index

r_next_ind:
     ; get the next register
     mov     cl, byte ptr ds:[bx+di]
     cmp     cl, 0ffh
     jz     r_all_written

     ; select the next register
     xchg     al, cl
     out     dx, al
     mov     al, cl

     ; and write it
     inc     dx
     out     dx, al
     shr     eax, 8
     dec     dx
     inc     di
     cmp     di, 4
     jne     r_next_ind

     ; need another 32 bits of data from the user buffer
     mov     eax, dword ptr gs:[si]
     add     si, 4
     add     bx, di
     xor     di, di
     jmp     r_next_ind

r_all_written:

     pop     cx
     pop     gs
     pop     edi
     pop     esi

     mov     ax, TRUE
     ret

restore_io_regs  ENDP

;*******************************************************************************
; Function:    save_palette IOCtl request #10
;
; Purpose:     saves the xga palette
;*******************************************************************************
PUBLIC save_palette
save_palette PROC NEAR


     push     esi
     push     gs
     push     cx

     ; get the pointer to our buffer
     lgs     si, es:[bx].GIODataPack

     ; get addressability to the hardware info
     mov     ax, SEG instances
     mov     es, ax
     mov     bx, OFFSET instances

     ; get the io register base
     mov     dx, word ptr es:[bx].XGA_io_base
     add     dx, index

     ; use R, G, B access method and start with R
     mov     ax, palette_rgb
     out     dx, ax

     ; start with entry zero
     xor     ax, ax
     mov     al, sprite_indx_read_lo
     out     dx, ax

     ; select the palette data register
     mov     al, palette_data
     out     dx, al

     ; we need to read 256 entries, 3 bytes per entry -> 768 in total
     mov     cx, SIZE_PALETTE
     inc     dx

@@:
     in     al, dx
     mov     byte ptr gs:[si], al
     inc     si
     dec     cx
     jnz     @B

     pop     cx
     pop     gs
     pop     esi

     mov     ax, TRUE
     ret

save_palette  ENDP

;*******************************************************************************
; Function:    restore_palette IOCtl request #11
;
; Purpose:     restores the xga palette
;*******************************************************************************
PUBLIC restore_palette
restore_palette PROC NEAR

     push     esi
     push     gs
     push     cx

     ; get the pointer to our buffer
     lgs     si, es:[bx].GIODataPack

     ; get addressability to the hardware info
     mov     ax, SEG instances
     mov     es, ax
     mov     bx, OFFSET instances

     ; get the io register base
     mov     dx, word ptr es:[bx].XGA_io_base
     add     dx, index

     ; use R, G, B access method and start with R
     mov     ax, palette_rgb
     out     dx, ax

     ; start with entry zero
     xor     ax, ax
     mov     al, sprite_indx_write_lo
     out     dx, ax

     ; select the palette data register
     mov     al, palette_data
     out     dx, al

     ; we need to write 256 entries, 3 bytes per entry -> 768 in total
     mov     cx, SIZE_PALETTE
     inc     dx

@@:
     mov     al, byte ptr gs:[si]
     out     dx, al
     inc     si
     dec     cx
     jnz     @B

     pop     cx
     pop     gs
     pop     esi

     mov     ax, TRUE
     ret

restore_palette  ENDP

;*******************************************************************************
; Function:  flat_lock
;
; Purpose:   locks a flat area of memory
;
; Entry:     On entry es:[bx] points to DevIoclt parm packet
;            The data packet within the parm packet contains the
;            address of the request parm block!
;*******************************************************************************
PUBLIC flat_lock
flat_lock  PROC NEAR

;/*  Get address of parm block from DevIoctl data packet
     lgs     di, es:[bx].GIODataPack

     ; get details of memory to lock
     mov     ebx, gs:[di].VirtualAddress
     mov     ecx, gs:[di].cMemorySize
     mov     eax, gs:[di].ActionFlags

     ; save the pointer in di before we pass a parameter in it
     push     di

     mov     edi, FlatPageList
     mov     esi, FlatLockHandle

     mov     dl, DevHlp_VMLock
     call     Device_Help

     ; restore our parm block pointer
     pop     di

     mov     eax, dword ptr LockHandle
     mov     gs:[di].Lockhandle1,eax
     mov     eax, dword ptr LockHandle+4
     mov     gs:[di].Lockhandle2,eax
     mov     eax, dword ptr LockHandle+8
     mov     gs:[di].Lockhandle3,eax

     mov     eax, dword ptr PageList
     mov     gs:[di].PhysicalAddress, eax

     mov     ax, TRUE
     ret

flat_lock  ENDP

;*******************************************************************************
; Function:  flat_unlock
;
; Purpose:   unlocks a flat area of memory
;
; Entry:     On entry es:[bx] points to DevIoclt parm packet
;            The data packet within the parm packet contains the
;            address of the request parm block!
;*******************************************************************************
PUBLIC flat_unlock
flat_unlock PROC NEAR

;/*  Get address of parm block from DevIoctl data packet
     lgs     di, es:[bx].GIODataPack

     ; get details of memory to unlock from the parameter block
     mov     eax, gs:[di].Lockhandle1
     mov     dword ptr LockHandle, eax
     mov     eax, gs:[di].Lockhandle2
     mov     dword ptr LockHandle+4, eax
     mov     eax, gs:[di].Lockhandle3
     mov     dword ptr LockHandle+8, eax

     mov     esi,FlatLockHandle

     mov     dl, DevHlp_VMUnlock
     call     Device_Help

     mov     ax, TRUE
     jc     unlock_no_error

     mov     ax, FALSE

unlock_no_error:
     ret

flat_unlock  ENDP

;*******************************************************************************
; Function:  flat_access
;
; Purpose:   provides flat model access for this process
;
; Entry:     On entry es:[bx] points to DevIoclt parm packet
;            The data packet within the parm packet contains the
;            address of the request parm block!
;*******************************************************************************
PUBLIC flat_access
flat_access PROC NEAR

;/*  Get address of parm block from DevIoctl data packet
     lgs     di, es:[bx].GIODataPack

     push     di

     jmp     works

     mov     eax,0001h          ; specify read/write access
     mov     ebx,instances.XGA_1M_aperture_flat
     mov     ecx,100000h          ; 1M
     mov     dl, DevHlp_VMGlobalToProcess
     call     Device_Help

     jmp     no_1M_aperture_available

works:
     mov     eax, instances.XGA_1M_aperture_phys
     and     eax,eax
     jz     no_1M_aperture_available

     ; get a linear address for the physical aperture
     mov     XGARegPointer, eax     ; put the physical address in place
     mov     eax, 00110000b          ; get process linear addr for phys addr
     mov     ecx, 100000h          ; aperture is 1M long
     mov     edi, FlatXGARegPointer
     mov     dl,DevHlp_VMAlloc
     call     Device_Help          ; eax = linear addr

no_1M_aperture_available:
     pop     di

     mov     gs:[di],eax          ; save the linear address

     mov     ax, TRUE
     jc     access_no_error

     mov     ax, FALSE

access_no_error:
     ret

flat_access  ENDP

;*******************************************************************************
; Function:  global_access
;
; Purpose:   provides global access to 32bit memory
;
; Entry:     On entry es:[bx] points to DevIoclt parm packet
;            The data packet within the parm packet contains the
;            address of the request parm block!
;*******************************************************************************
PUBLIC global_access
global_access  PROC NEAR

;/*  Get address of parm block from DevIoctl data packet
     lgs     di, es:[bx].GIODataPack

     push     di

     mov     eax, instances.XGA_1M_aperture_phys
     and     eax,eax
     jz     global_no_1M_aperture

     ; get a linear address for the physical aperture
     mov     XGARegPointer, eax     ; put the physical address in place
     mov     eax, 00010000b          ; get global linear addr for phys addr
     mov     ecx, 100000h          ; aperture is 1M long
     mov     edi, FlatXGARegPointer
     mov     dl,DevHlp_VMAlloc
     call     Device_Help          ; eax = linear addr

global_no_1M_aperture:
     pop     di

     mov     gs:[di],eax          ; save the linear address

     mov     ax, TRUE
     jc     global_access_no_error

     mov     ax, FALSE

global_access_no_error:
     ret

global_access  ENDP

;*******************************************************************************
; Function:  create_callgate
;
; Purpose:   set up a call gate to get flat model ring 0 code
;
; Entry:     On entry es:[bx] points to DevIoclt parm packet
;            The data packet within the parm packet contains the
;            address of the request parm block!
;*******************************************************************************
PUBLIC create_callgate
create_callgate PROC NEAR

;/*  Get address of parm block from DevIoctl data packet
     lgs     di, es:[bx].GIODataPack

; We try setting up a direct Call Gate into some 32 bit code in the
; ring0 device driver.

     mov     eax, gs:[di]     ; get the linear address
     mov     ecx, gs:[di+4]     ; number of parameters

     push     di

     mov     bx,ax          ; low order
     shr     eax,16          ; high order
     mov     dh, 0          ; 32 bit callgate, linear worker
     mov     dl, DevHlp_DynamicAPI
     call     Device_Help     ; returns di = callgate selector

     pop     bx

     mov     dword ptr gs:[bx+8], 0
     mov     gs:[bx+12], di

     mov     ax, TRUE
     ret

create_callgate  ENDP

;*******************************************************************************
; Function:     UNSUPPORTED
;
; Purpose:     Set the drvError to Error + Unknown Command.
;              This is set in the Request Header STATUS before returning.
;*******************************************************************************
PUBLIC Unsupported
Unsupported PROC NEAR

     mov     ax, UNKNOWN_COMMAND
     ret

Unsupported  ENDP

;*******************************************************************************
; Function:     OPEN
;
; Purpose:     Does nothing. The OS/2 return status is set to DONE.
;*******************************************************************************
PUBLIC Open
Open PROC NEAR

     mov     ax, NO_ERROR
     ret

Open  ENDP

;*******************************************************************************
; Function:     CLOSE
;
; Purpose:     Does nothing. The OS/2 return status is set to DONE.
;*******************************************************************************
PUBLIC Close
Close  PROC NEAR

     mov     ax, NO_ERROR
     ret

Close  ENDP

;*******************************************************************************
; Function:     GetDeviceSupport
;
; Purpose:     Returns Device support flags ( > 16M memory supported )
;*******************************************************************************
PUBLIC GetDeviceSupport
GetDeviceSupport PROC NEAR

; Set device support flags = >16M memory supported

     mov     es: [bx].RPDSFlags,DevSupp_GT16M  ;support flags
     mov     ax, NO_ERROR

     ret

GetDeviceSupport  ENDP

;*******************************************************************************
; Function:     DoPhysToUVirt
;
; Purpose:     Using the globals ulPhysicalAddress, usLength, and
;              pResult,  passes data to and from a call to PhysToUVirt.
;              This allows C code to call PhysToUVirt in an easy manner.
;*******************************************************************************
PUBLIC DoPhysToUVirt
DoPhysToUVirt  PROC NEAR

     push     es
     push     eax
     push     bx
     push     cx
     push     dx

     ; Get the physical address into the ax(hi)bx(lo) pair.
     mov     eax, ulPhysicalAddress
     mov     bx, ax
     shr     eax, 16

     ; Get the length of the requested block into cx.
     mov     cx, usSize

     ; Set the request type to be "read/write virtual with IOPL"
     mov     dl, DevHlp_PhysToUVirt
     mov     dh, 4

     ; Make the call. The result is returned as es:bx.
     call     Device_Help

     ; If the carry is clear then we were successful.
     jnc     dptuv_noerror

     ; If there was an error then we want to return a null pointer.
     mov     pResult, 0
     jmp     dptuv_gotresult

dptuv_noerror:
     mov     ax, es
     shl     eax, 16
     mov     ax, bx
     mov     pResult, eax

dptuv_gotresult:

     pop     dx
     pop     cx
     pop     bx
     pop     eax
     pop     es
     ret

DoPhysToUVirt  ENDP

;*******************************************************************************
; Function:     DoGetDosVar
;
; Purpose:     This routine will attempt to obtain the DMQS information
;              via the OS/2 kernel. If successful the global variable
;              pResult will be updated to point to the data.
;
;              This allows C code to call GetDOSVar in an easy manner.
;*******************************************************************************
PUBLIC DoGetDOSVar
DoGetDOSVar PROC NEAR

     push     eax
     push     dx
     push     es

     ; We want the DMQS selector
     mov     al, DHGETDOSV_DMQSSEL

     mov     dl, DevHlp_GetDOSVar

     ; Make the call. A pointer to the Selector is in ax:bx.
     call     Device_Help

     ; If the carry is clear then we were successful.
     jnc     dgdv_noerror

     ; If there was an error then we want to return a null pointer.
     mov     pResult, 0
     jmp     dgdv_gotresult


dgdv_noerror:
     ; At this point AX:BX points to the DMSQ selector
     ; The data starts at offset 0 from the DMQS selector
     mov     es, ax
     mov     ax, word ptr es:[bx]
     shl     eax, 16
     mov     pResult, eax

dgdv_gotresult:

     pop     es
     pop     dx
     pop     eax
     ret

DoGetDOSVar  ENDP

_TEXT     ENDS
END
