;------------------------------------------------------------------------------
; @file
; A minimal Int10h stub that allows the Windows 2008 R2 SP1 UEFI guest's buggy,
; default VGA driver to switch to 1024x768x32, on the stdvga and QXL video
; cards of QEMU.
;
; Copyright (C) 2014, Red Hat, Inc.
; Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
;
; This program and the accompanying materials are licensed and made available
; under the terms and conditions of the BSD License which accompanies this
; distribution.  The full text of the license may be found at
; http://opensource.org/licenses/bsd-license.php
;
; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
; WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
;
;------------------------------------------------------------------------------

; enable this macro for debug messages
;%define DEBUG

%macro DebugLog 1
%ifdef DEBUG
  push       si
  mov        si, %1
  call       PrintStringSi
  pop        si
%endif
%endmacro


BITS 16
ORG 0

VbeInfo:
TIMES 256 nop

VbeModeInfo:
TIMES 256 nop


Handler:
  cmp        ax, 0x4f00
  je         GetInfo
  cmp        ax, 0x4f01
  je         GetModeInfo
  cmp        ax, 0x4f02
  je         SetMode
  cmp        ax, 0x4f03
  je         GetMode
  cmp        ax, 0x4f10
  je         GetPmCapabilities
  cmp        ax, 0x4f15
  je         ReadEdid
  cmp        ah, 0x00
  je         SetModeLegacy
  DebugLog   StrUnkownFunction
Hang:
  jmp        Hang


GetInfo:
  push       es
  push       di
  push       ds
  push       si
  push       cx

  DebugLog   StrEnterGetInfo

  ; target (es:di) set on input
  push       cs
  pop        ds
  mov        si, VbeInfo
  ; source (ds:si) set now

  mov        cx, 256
  cld
  rep movsb

  pop        cx
  pop        si
  pop        ds
  pop        di
  pop        es
  jmp        Success


GetModeInfo:
  push       es
  push       di
  push       ds
  push       si
  push       cx

  DebugLog   StrEnterGetModeInfo

  and        cx, ~0x4000 ; clear potentially set LFB bit in mode number
  cmp        cx, 0x00f1
  je         KnownMode1
  DebugLog   StrUnkownMode
  jmp        Hang
KnownMode1:
  ; target (es:di) set on input
  push       cs
  pop        ds
  mov        si, VbeModeInfo
  ; source (ds:si) set now

  mov        cx, 256
  cld
  rep movsb

  pop        cx
  pop        si
  pop        ds
  pop        di
  pop        es
  jmp        Success


%define ATT_ADDRESS_REGISTER   0x03c0
%define VBE_DISPI_IOPORT_INDEX 0x01ce
%define VBE_DISPI_IOPORT_DATA  0x01d0

%define VBE_DISPI_INDEX_XRES        0x1
%define VBE_DISPI_INDEX_YRES        0x2
%define VBE_DISPI_INDEX_BPP         0x3
%define VBE_DISPI_INDEX_ENABLE      0x4
%define VBE_DISPI_INDEX_BANK        0x5
%define VBE_DISPI_INDEX_VIRT_WIDTH  0x6
%define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7
%define VBE_DISPI_INDEX_X_OFFSET    0x8
%define VBE_DISPI_INDEX_Y_OFFSET    0x9

%define VBE_DISPI_ENABLED     0x01
%define VBE_DISPI_LFB_ENABLED 0x40

%macro BochsWrite 2
  push       dx
  push       ax

  mov        dx, VBE_DISPI_IOPORT_INDEX
  mov        ax, %1
  out        dx, ax

  mov        dx, VBE_DISPI_IOPORT_DATA
  mov        ax, %2
  out        dx, ax

  pop        ax
  pop        dx
%endmacro

SetMode:
  push       dx
  push       ax

  DebugLog   StrEnterSetMode

  cmp        bx, 0x40f1
  je         KnownMode2
  DebugLog   StrUnkownMode
  jmp        Hang
KnownMode2:

  ; unblank
  mov        dx, ATT_ADDRESS_REGISTER
  mov        al, 0x20
  out        dx, al

  BochsWrite VBE_DISPI_INDEX_ENABLE,        0
  BochsWrite VBE_DISPI_INDEX_BANK,          0
  BochsWrite VBE_DISPI_INDEX_X_OFFSET,      0
  BochsWrite VBE_DISPI_INDEX_Y_OFFSET,      0
  BochsWrite VBE_DISPI_INDEX_BPP,          32
  BochsWrite VBE_DISPI_INDEX_XRES,       1024
  BochsWrite VBE_DISPI_INDEX_VIRT_WIDTH, 1024
  BochsWrite VBE_DISPI_INDEX_YRES,        768
  BochsWrite VBE_DISPI_INDEX_VIRT_HEIGHT, 768
  BochsWrite VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED

  pop        ax
  pop        dx
  jmp        Success


GetMode:
  DebugLog   StrEnterGetMode
  mov        bx, 0x40f1
  jmp        Success


GetPmCapabilities:
  DebugLog   StrGetPmCapabilities
  jmp        Unsupported


ReadEdid:
  DebugLog   StrReadEdid
  jmp        Unsupported


SetModeLegacy:
  DebugLog   StrEnterSetModeLegacy

  cmp        al, 0x03
  je         KnownMode3
  cmp        al, 0x12
  je         KnownMode4
  DebugLog   StrUnkownMode
  jmp        Hang
KnownMode3:
  mov        al, 0x30
  jmp        SetModeLegacyDone
KnownMode4:
  mov        al, 0x20
SetModeLegacyDone:
  DebugLog   StrExitSuccess
  iret


Success:
  DebugLog   StrExitSuccess
  mov        ax, 0x004f
  iret


Unsupported:
  DebugLog   StrExitUnsupported
  mov        ax, 0x014f
  iret


%ifdef DEBUG
PrintStringSi:
  pusha
  push       ds ; save original
  push       cs
  pop        ds
  mov        dx, 0x0402
PrintStringSiLoop:
  lodsb
  cmp        al, 0
  je         PrintStringSiDone
  out        dx, al
  jmp        PrintStringSiLoop
PrintStringSiDone:
  pop        ds ; restore original
  popa
  ret


StrExitSuccess:
  db 'Exit', 0x0a, 0

StrExitUnsupported:
  db 'Unsupported', 0x0a, 0

StrUnkownFunction:
  db 'Unknown Function', 0x0a, 0

StrEnterGetInfo:
  db 'GetInfo', 0x0a, 0

StrEnterGetModeInfo:
  db 'GetModeInfo', 0x0a, 0

StrEnterGetMode:
  db 'GetMode', 0x0a, 0

StrEnterSetMode:
  db 'SetMode', 0x0a, 0

StrEnterSetModeLegacy:
  db 'SetModeLegacy', 0x0a, 0

StrUnkownMode:
  db 'Unkown Mode', 0x0a, 0

StrGetPmCapabilities:
  db 'GetPmCapabilities', 0x0a, 0

StrReadEdid:
  db 'ReadEdid', 0x0a, 0
%endif