/*         ______   ___    ___ 
 *        /\  _  \ /\_ \  /\_ \ 
 *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___ 
 *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
 *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
 *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
 *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
 *                                           /\____/
 *                                           \_/__/
 *
 *      VESA bank switching code. These routines will be called with
 *      a line number in %eax and a pointer to the bitmap in %edx. The
 *      bank switcher should select the appropriate bank for the line,
 *      and replace %eax with a pointer to the start of the line.
 *
 *      By Shawn Hargreaves.
 *
 *      See readme.txt for copyright information.
 */


#include "../i386/asmdefs.inc"

.text




/* Template for bank switchers. Produces a framework that checks which 
 * bank is currently selected and only executes the provided code if it
 * needs to change bank.
 */
#define BANK_SWITCHER(name, cache, code)                                     \
   pushl %eax                                                              ; \
									   ; \
   addl BMP_YOFFSET(%edx), %eax              /* add line offset */         ; \
									   ; \
   shll $2, %eax                                                           ; \
   addl GLOBL(_gfx_bank), %eax               /* lookup which bank */       ; \
   movl (%eax), %eax                                                       ; \
   cmpl cache, %eax                          /* need to change? */         ; \
   je name##_done                                                          ; \
									   ; \
   movl %eax, cache                          /* store the new bank */      ; \
   code                                      /* and change it */           ; \
									   ; \
   _align_                                                                 ; \
name##_done:                                                               ; \
   popl %eax                                                               ; \
   movl BMP_LINE(%edx, %eax, 4), %eax        /* load line address */




/* Uses VESA function 05h (real mode interrupt) to select the bank in %eax.
 */
#define SET_VESA_BANK_RM(window)                                             \
   pushal                                    /* store registers */         ; \
   pushw %es                                                               ; \
									   ; \
   movw GLOBL(__djgpp_ds_alias), %bx                                       ; \
   movw %bx, %es                                                           ; \
									   ; \
   movl $0x10, %ebx                          /* call int 0x10 */           ; \
   movl $0, %ecx                             /* no stack required */       ; \
   movl $GLOBL(_dpmi_reg), %edi              /* register structure */      ; \
									   ; \
   movw $0x4F05, DPMI_AX(%edi)               /* VESA function 05h */       ; \
   movw $window, DPMI_BX(%edi)               /* which window? */           ; \
   movw %ax, DPMI_DX(%edi)                   /* which bank? */             ; \
   movw $0, DPMI_SP(%edi)                    /* zero stack */              ; \
   movw $0, DPMI_SS(%edi)                                                  ; \
   movw $0, DPMI_FLAGS(%edi)                 /* and zero flags */          ; \
									   ; \
   movl $0x0300, %eax                        /* simulate RM interrupt */   ; \
   int $0x31                                                               ; \
									   ; \
   popw %es                                  /* restore registers */       ; \
   popal




/* VESA 1.x window 1 bank switcher */
FUNC(_vesa_window_1)
   BANK_SWITCHER(vesa_window_1, GLOBL(_last_bank_1), SET_VESA_BANK_RM(0))
   ret

FUNC(_vesa_window_1_end)
   ret




/* VESA 1.x window 2 bank switcher */
FUNC(_vesa_window_2)
   BANK_SWITCHER(vesa_window_2, GLOBL(_last_bank_2), SET_VESA_BANK_RM(1))
   addl GLOBL(_window_2_offset), %eax
   ret

FUNC(_vesa_window_2_end)
   ret




/* Uses the VESA 2.0 protected mode interface to select the bank in %eax.
 */
#define SET_VESA_BANK_PM(window)                                             \
   pushal                                    /* store registers */         ; \
									   ; \
   movw $window, %bx                         /* which window? */           ; \
   movw %ax, %dx                             /* which bank? */             ; \
   movl GLOBL(_pm_vesa_switcher), %eax                                     ; \
   call *%eax                                /* do it! */                  ; \
									   ; \
   popal                                     /* restore registers */




/* VESA 2.0 window 1 bank switcher */
FUNC(_vesa_pm_window_1)
   BANK_SWITCHER(vesa_pm_window_1, GLOBL(_last_bank_1), SET_VESA_BANK_PM(0))
   ret

FUNC(_vesa_pm_window_1_end)
   ret




/* VESA 2.0 window 2 bank switcher */
FUNC(_vesa_pm_window_2)
   BANK_SWITCHER(vesa_pm_window_2, GLOBL(_last_bank_2), SET_VESA_BANK_PM(1))
   addl GLOBL(_window_2_offset), %eax
   ret

FUNC(_vesa_pm_window_2_end)
   ret




/* Uses the VESA 2.0 protected mode interface to select the bank in %eax,
 * passing a selector for memory mapped io in %es.
 */
#define SET_VESA_BANK_PM_ES(window)                                          \
   pushal                                    /* store registers */         ; \
   pushw %es                                                               ; \
									   ; \
   movw $window, %bx                         /* which window? */           ; \
   movw %ax, %dx                             /* which bank? */             ; \
   movw GLOBL(_mmio_segment), %ax            /* load selector into %es */  ; \
   movw %ax, %es                                                           ; \
   movl GLOBL(_pm_vesa_switcher), %eax                                     ; \
   call *%eax                                /* do it! */                  ; \
									   ; \
   popw %es                                  /* restore registers */       ; \
   popal




/* VESA 2.0 MMIO mode window 2 bank switcher */
FUNC(_vesa_pm_es_window_1)
   BANK_SWITCHER(vesa_pm_es_window_1, GLOBL(_last_bank_1), SET_VESA_BANK_PM_ES(0))
   ret

FUNC(_vesa_pm_es_window_1_end) 
   ret




/* VESA 2.0 MMIO mode window 2 bank switcher */
FUNC(_vesa_pm_es_window_2)
   BANK_SWITCHER(vesa_pm_es_window_2, GLOBL(_last_bank_2), SET_VESA_BANK_PM_ES(1))
   addl GLOBL(_window_2_offset), %eax
   ret

FUNC(_vesa_pm_es_window_2_end)
   ret

