;
;   MenuetOS Driver (vmode.mdr)
;   Target: Vertical Refresh Rate programming and videomode changing
;
;   Author: Trans <<<<<13>>>>>
;   Date:   20.07.2003
;
;   Version: 1.0
;   OS: MenuetOS
;   Compiler: FASM
;

OS_BASE equ 0x80000000

use32

macro align value { rb (value-1) - ($ + value-1) mod value }

        org    OS_BASE+0x0328000

headerstart=$

mdid    db     'MDAZ'              ; 4 byte id
mdhver  dd     0x00                ; header version
mdcode  dd     MDSTART             ; start of code
mdver   dd     0x00000001          ; driver version (subversion*65536+version)
mdname  db     'Trans VideoDriver' ; 32 bytes of full driver name
    times (32-($-mdname)) db ' '   ;

headerlen=$-headerstart
    times (256-headerlen) db 0     ; reserved area for future

MDSTART:    ; start of driver code ( base_adr+256 bytes)
; ebx(=ecx in program):
;   1 - Get DriverInfo and Driver Initial Set
;   2 - Get Current Video Mode With Vertical Refresh Rate
;   3 - Change Video Mode
;   4 - Return at Start System Video Mode
;   5 - Change vertical and horizontal size of visible screen area
;   6 - Change Vert/Hor position visible area on screen (not complete yet)
;
;   MAXF - ...
MAXF=5

;-------Main Manager-------------
    pushad
    cmp ebx,1
    jb mdvm_00
    cmp ebx,MAXF
    ja mdvm_00
    shl ebx,2
    add ebx,mdvm_func_table
    call dword [ebx]
    mov [esp+28],eax
    mov [esp+24],ecx
    mov [esp+20],edx
    mov [esp+16],ebx
    popad
    retn
mdvm_00:
    popad
    xor eax,eax
    dec eax
    retn

; ------Drivers Functions----------

align 4

; EBX=1 (in applications ECX=1)- Get DriverInfo and Driver Initial Set
;
; IN: ecx (in app. edx) - pointer to 512-bytes info area in application
; OUT:
;
vm_info_init:
      push ecx
      cmp [mdrvm],dword 0
      jnz .vmii_00
      call vm_safe_reg
      call vm_get_initial_videomode
      mov eax,[initvm]
      mov [currvm],eax
      call vm_search_sys_func_table
      call vm_get_cur_vert_rate
      mov [initrr],eax
      call vm_calc_pixelclock
      call vm_calc_refrate
      inc [mdrvm]
.vmii_00:
      pop ecx
      call vm_transfer_drv_info
      mov ebx,dword [refrate]
      mov eax,dword [mdid]      ;dword [systlb]
      retn


align 4

; EBX=2 (in applications ECX=2)- Get Current Video Mode
;
; OUT: eax = X_screen*65536+Y_screen
;      ebx = current vertical rate
;      ecx = current video mode (number)
vm_get_cur_mode:
      cmp [mdrvm],dword 0
      jz .vmgcm_00
      call vm_get_cur_vert_rate
      mov eax,[OS_BASE+0FE00h]
      mov ebx,[OS_BASE+0FE04h]
      shl eax,16
      add eax,ebx
      add eax,00010001h
      mov ebx,[refrate]
      mov ecx,[currvm]
      retn
.vmgcm_00:
      xor eax,eax
      dec eax
      retn


align 4

; EBX=3 (in applications ECX=3)- Change Video Mode
;
; IN:  ecx = VertRate*65536+VideoMode
; OUT: eax = 0 if no error
;
vm_set_video_mode:
      cmp [mdrvm],dword 0
      jz .vmsvm_00
      call vm_set_selected_mode
;      xor eax,eax
      retn
.vmsvm_00:
      xor eax,eax
      dec eax
      retn


align 4

; EBX=4 (in applications ECX=4)- Return at Start System Video Mode
;
; IN:
; OUT: eax = = 0 if no error
;
vm_restore_init_video_mode:
      cmp [mdrvm],dword 0
      jz .vmrivm_00
      call vm_restore_reg
      xor eax,eax
      retn
.vmrivm_00:
      xor eax,eax
      dec eax
      retn


align 4

; EBX=5 (in applications ECX=5)- Change vertical and horizontal size
;                                 of visible screen area
; IN:  ecx (in app. edx) = 0/1 - -/+ horizontal size on 1 position
;                        = 2/3 - -/+ vertical size on 1 position (8 pixels)
;                          ^-^----- not complete yet
; OUT: eax = = 0 if no error
;
vm_change_screen_size:
      cmp [mdrvm],dword 0
      jz .vmcss_00
      cmp cl,1
      ja .vmcss_01
      mov eax,ecx
      call vm_inc_dec_width
      xor eax,eax
      retn
.vmcss_01:
      and ecx,01h
      mov eax,ecx
;     call vm_inc_dec_high   ; not complete yet
      xor eax,eax
      retn
.vmcss_00:
      xor eax,eax
      dec eax
      retn


align 4

; EBX=6 (in applications ECX=6)- Change Vert/Hor position visible area on screen
;
; IN:  ecx (in app. edx) = 0/1 - -/+ horizontal position on 1 point
;                        = 2/3 - -/+ vertical position on 1 pixel
;                          ^-^----- not complete yet
; OUT: eax = 0 if no error
;
vm_change_position_screen:
      cmp [mdrvm],dword 0
      jz .vmcps_00
  ; ...
      xor eax,eax
      retn
.vmcps_00:
      xor eax,eax
      dec eax
      retn


;-----Drivers Subfunctions---------

;
; Searching i40 system functions pointer table in kernel area location
;
vm_search_sys_func_table:
      push eax     ; eax - current value
      push ecx     ; ecx - will be counter of equevalent value
      push edx     ; edx - last value
      push esi     ; esi - current address
      xor ecx,ecx
      mov esi,OS_BASE+010000h            ; Start address of kernel location
      lodsd
      mov edx,eax
      cld
.vmssft_00:
      cmp esi,OS_BASE+30000h
      ja .vmssft_03
      inc ecx
      lodsd
      cmp edx,eax
      mov edx,eax
      je .vmssft_00
      cmp ecx,128
      ja .vmssft_02
.vmssft_01:
      xor ecx,ecx
      jmp .vmssft_00
.vmssft_02:
      cmp edx,0
      je .vmssft_01
      sub esi,256*4-1
      mov [systlb],esi
      xor ecx,ecx
.vmssft_03_0:
      inc ecx
      lodsd
      cmp edx,eax
      mov edx,eax
      jne .vmssft_03_0
      mov esi,dword [systlb]
      cmp cx,60
      jae .vmssft_03
      add esi,256*4-4
      lodsb
      mov edx,eax
      jmp .vmssft_01
.vmssft_03:
      mov [systlb],esi
      pop esi
      pop edx
      pop ecx
      pop eax
      retn

; IN:
; OUT: eax= vertical rate in Hz
vm_get_cur_vert_rate:
      push edx
      push ebx
      xor eax,eax
      mov edx,eax
      mov ebx,eax
      mov dx,03DAh
.vmgcvt_00:
      in al,dx
      test al,8
      jz .vmgcvt_00
.vmgcvt_01:
      in al,dx
      test al,8
      jnz .vmgcvt_01
      mov ebx,edx
      rdtsc
      mov edx,ebx
      mov ebx,eax
.vmgcvt_02:
      in al,dx
      test al,8
      jz .vmgcvt_02
.vmgcvt_03:
      in al,dx
      test al,8
      jnz .vmgcvt_03
      rdtsc
      sub eax,ebx
      mov ebx,eax
      mov eax,[OS_BASE+0F600h]
      xor edx,edx
      div ebx
      inc eax
      mov [refrate],eax
      pop ebx
      pop edx
      retn

vm_calc_pixelclock:
        push ebx
        push edx
        xor eax,eax
        mov al,[_00]
        add ax,5
        shl eax,3
        xor ebx,ebx
        mov bl,[_06]
        mov bh,[_07]
        and bh,00100001b
        btr bx,13
        jnc .vmcpc_00
        or bh,2
.vmcpc_00:
        xor edx,edx
        mul ebx
        xor edx,edx
        mul [initrr]
        mov [pclock],eax
        pop edx
        pop ebx
        retn

;
; Safe of initial CRTC state
;
vm_safe_reg:
    push edx
    push ebx
    push ecx
    push edi
    cli
    mov dx,3d4h  ; CRTC
    mov al,11h
    out dx,al
    inc dx
    in al,dx
    and al,7fh
    out dx,al    ; Clear protection bit
    dec dx
    xor ecx,ecx
    mov cl,19h
    xor bl,bl
    mov edi,CRTCreg
.vmsr_00:
    mov al,bl
    out dx,al
    inc dx
    in al,dx
    dec dx
    stosb
    inc bl
    loop .vmsr_00
    sti
    pop edi
    pop ecx
    pop ebx
    pop edx
    retn

;
; Restore of initial CRTC state
;
vm_restore_reg:
    push eax
    push ebx
    push edx
    push esi
    mov eax,[oldX]
    mov [OS_BASE+0FE00h],eax
    mov eax,[oldY]
    mov [OS_BASE+0FE04h],eax
    mov dx,03dah
.vmrr_00:
    in al,dx
    test al,8
    jnz .vmrr_00
.vmrr_01:
    in al,dx
    test al,8
    jnz .vmrr_01
    cli
    mov dx,03c4h
    mov ax,0101h
    out dx,ax
    mov dx,3d4h  ; CRTC
    mov al,11h
    out dx,al
    inc dx
    in al,dx
    and al,7fh  ; Clear Protection bit
    out dx,al
    dec dx
    xor ecx,ecx
    mov cl,19h
    mov esi,CRTCreg
    xor bl,bl
.vmrr_02:
    lodsb
    mov ah,al
    mov al,bl
    out dx,ax
    inc bl
    loop .vmrr_02
    sti
;    call ref_screen
    pop esi
    pop edx
    pop ecx
    pop eax
    retn

; Calculate of possible vertical refrash rate
;  (light version of function)
vm_calc_refrate:
        push ebx
        push ecx
        push edx
        push edi
        push esi
        mov eax,[pclock]
        xor edx,edx
        mov edi,_m1
        mov ebx,eax
        mov ecx,(1696*1065)
        div ecx
        xor edx,edx
        stosw
        add edi,8
        mov eax,ebx
        mov ecx,(1344*804)
        div ecx
        xor edx,edx
        stosw
        add edi,8
        mov eax,ebx
        mov ecx,(1056*636)
        div ecx
        xor edx,edx
        stosw
        add edi,8
        mov eax,ebx
        mov ecx,(800*524)
        div ecx
        xor edx,edx
        stosw
        mov edi,_m1
        mov esi,edi
        mov ecx,5*4
.vmcrr_00:
        lodsw
        cmp ax,55
        jb .vmcrr_01
        stosw
        loop .vmcrr_00
        pop esi
        pop edi
        pop edx
        pop ecx
        pop ebx
        retn
.vmcrr_01:
        xor ax,ax
        stosw
        loop .vmcrr_00
        pop esi
        pop edi
        pop edx
        pop ecx
        pop ebx
        retn

vm_get_initial_videomode:
    push eax
    mov eax,dword [OS_BASE+0FE00h]
    mov [oldX],eax
    mov eax,dword [OS_BASE+0FE04h]
    mov [oldY],eax
    mov eax,dword [OS_BASE+0FE0Ch] ; initial video mode
    and ax,01FFh
    mov dword [initvm],eax
    pop eax
    retn


; IN: eax = 0/1  -  -/+ 1 position of width
vm_inc_dec_width:
    push ebx
    push edx
    mov ebx,eax
    mov dx,3d4h  ; CRTC
    mov al,11h
    out dx,al
    inc dx
    in al,dx
    and al,7fh  ; Clear Protection bit
    out dx,al
    dec dx
    xor al,al
    out dx,al
    inc dx
    in al,dx
    dec al
    cmp bl,0
    jnz .vmidr_00
    inc al
    inc al
.vmidr_00:
    out dx,al
    pop edx
    pop ebx
    retn

;
; Copy driver info to application area
;
; IN: ecx (in app. edx) - pointer to 512-bytes info area in application
; OUT:
vm_transfer_drv_info:
        push ecx
        push edi
        push esi
        mov eax,ecx
        xor ecx,ecx
        mov cl,32/4
        mov esi,mdname
        mov edi,drvname
        rep movsd
        mov ecx,eax
        mov eax,[mdver]
        mov [drvver],eax
        mov edi,[OS_BASE+3010h]
        mov edi,[edi+10h]
        add edi,ecx
        mov esi,drvinfo
        xor ecx,ecx
        mov cx,512
        rep movsb
        pop esi
        pop edi
        pop ecx
        retn


;
; Set selected video mode
; (light version)
;
; IN: ecx = VertRate*65536+VideoMode
;
vm_set_selected_mode:
    push edx
    push ecx
    push esi
    ror ecx,16
    cmp cx,00h
    je .vmssm_03
    rol ecx,16
    mov eax,ecx
    shl eax,16
    shr eax,16
    mov [currvm],eax
    cmp cx,112h
    jne .vmssm_00
    mov esi,mode0
    mov ecx,639
    mov edx,479
    jmp .vmssm_st00
.vmssm_00:
    cmp cx,115h
    jne .vmssm_01
    mov esi,mode1
    mov ecx,799
    mov edx,599
    jmp .vmssm_st00
.vmssm_01:
    cmp cx,118h
    jne .vmssm_02
    mov esi,mode2
    mov ecx,1023
    mov edx,767
    jmp .vmssm_st00
.vmssm_02:
    cmp cx,11Bh
    jne .vmssm_03
    mov esi,mode2
    mov ecx,1279
    mov edx,1023
    jmp .vmssm_st00
.vmssm_03:
    xor eax,eax
    dec eax
    pop esi
    pop ecx
    pop edx
    retn
.vmssm_st00:
    mov [OS_BASE+0FE00h],ecx
    mov [OS_BASE+0FE04h],edx
    cli
    mov dx,03c4h
    lodsw
    out dx,ax
    mov dx,03d4h
    mov al,11h
    out dx,al
    inc dx
    in al,dx
    and al,7fh
    out dx,al
    dec dx
    mov ecx,13
.vmssm_st01:
    lodsw
    out dx,ax
    loop .vmssm_st01
    sti
    xor eax,eax
    pop esi
    pop ecx
    pop edx
    retn


;------------DATA AREA---------------
align 4

mdvm_func_table:
        dd MDSTART
        dd vm_info_init,          vm_get_cur_mode
        dd vm_set_video_mode,     vm_restore_init_video_mode
        dd vm_change_screen_size, vm_change_position_screen


CRTCreg:
_00  db ?
_01  db ?
_02  db ?
_03  db ?
_04  db ?
_05  db ?
_06  db ?
_07  db ?
_08  db ?
_09  db ?
_0a  db ?
_0b  db ?
_0c  db ?
_0d  db ?
_0e  db ?
_0f  db ?
_10  db ?
_11  db ?
_12  db ?
_13  db ?
_14  db ?
_15  db ?
_16  db ?
_17  db ?
_18  db ?
_19  db ?

align 4

oldX       dd ?
oldY       dd ?
initvm     dd ?
currvm     dd 0
refrate    dd 0
initrr     dd 0
systlb     dd 0
pclock     dd ?
mdrvm      dd 0 ; 0 - not drv init yet, 1 - already drv init


drvinfo:
drvname:   times 32 db ' '
drvver     dd 0
           times (32-($-drvver))/4 dd 0
drvmode    dw 011Bh,0118h,0115h,0112h
           times (64-($-drvmode))/2 dw 00h
_m1        dw 0,0,0,0,0
_m2        dw 0,0,0,0,0
_m3        dw 0,0,0,0,0
_m4        dw 0,0,0,0,0
_m5        dw 0,0,0,0,0
           times (512-($-drvinfo)) db 0
drvinfoend:


;1280x1024 - 11Bh
mode3:
      dw 0101h
      dw 0d000h,9f01h,9f02h,9303h,0a904h,1905h,2806h,5a07h
      dw 0110h,8411h,0ff12h,0ff15h,2916h

;1024x768 - 118h
mode2:
      dw 0101h
      dw 0a400h,7f01h,7f02h,8703h,8404h,9505h,2406h,0f507h
      dw 0310h,8911h,0ff12h,0ff15h,2516h

;800x600  - 115h
mode1:
      dw 0101h
      dw 8000h,6301h,6302h,8303h,6a04h,1a05h,7206h,0f007h
      dw 5910h,8d11h,5712h,5715h,7316h

;640x480 - 112h, 12h
mode0:
      dw 0101h
      dw 6000h,4f01h,4f02h,8303h,5304h,9f05h,00b06h,3e07h
      dw 0ea10h,8c11h,0df12h,0df15h,0c16h

; 640x400
;mymode0:
; dw 0101h
;_0_7 dw 5f00h,4f01h,4f02h,8303h,5304h,9f05h,0BF06h,1f07h
; dw 9c10h,8e11h,8f12h,9615h,0B916h ;,4013h

; 640x800
;mymode1:
; dw 0101h
; dw 5f00h,4f01h,4f02h,8003h,5004h,9f05h,06006h,0FF07h
; dw 2d10h,8f11h,2012h,2615h,05716h ;,4013h


DRVM_END: