Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / vgabios / vgabios.c
diff --git a/qemu/roms/vgabios/vgabios.c b/qemu/roms/vgabios/vgabios.c
new file mode 100644 (file)
index 0000000..c1e312b
--- /dev/null
@@ -0,0 +1,3923 @@
+// ============================================================================================
+/*
+ * vgabios.c
+ */
+// ============================================================================================
+//  
+//  Copyright (C) 2001-2008 the LGPL VGABios developers Team
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU Lesser General Public
+//  License as published by the Free Software Foundation; either
+//  version 2 of the License, or (at your option) any later version.
+//
+//  This library is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  Lesser General Public License for more details.
+//
+//  You should have received a copy of the GNU Lesser General Public
+//  License along with this library; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+// 
+// ============================================================================================
+//  
+//  This VGA Bios is specific to the plex86/bochs Emulated VGA card. 
+//  You can NOT drive any physical vga card with it. 
+//     
+// ============================================================================================
+//  
+//  This file contains code ripped from :
+//   - rombios.c of plex86 
+//
+//  This VGA Bios contains fonts from :
+//   - fntcol16.zip (c) by Joseph Gil avalable at :
+//      ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
+//     These fonts are public domain 
+//
+//  This VGA Bios is based on information taken from :
+//   - Kevin Lawton's vga card emulation for bochs/plex86
+//   - Ralf Brown's interrupts list available at http://www.cs.cmu.edu/afs/cs/user/ralf/pub/WWW/files.html
+//   - Finn Thogersons' VGADOC4b available at http://home.worldonline.dk/~finth/
+//   - Michael Abrash's Graphics Programming Black Book
+//   - Francois Gervais' book "programmation des cartes graphiques cga-ega-vga" edited by sybex
+//   - DOSEMU 1.0.1 source code for several tables values and formulas
+//
+// Thanks for patches, comments and ideas to :
+//   - techt@pikeonline.net
+//
+// ============================================================================================
+
+#include "vgabios.h"
+
+#ifdef VBE
+#include "vbe.h"
+#endif
+
+#define USE_BX_INFO
+
+/* Declares */
+static Bit8u          read_byte();
+static Bit16u         read_word();
+static void           write_byte();
+static void           write_word();
+static Bit8u          inb();
+static Bit16u         inw();
+static void           outb();
+static void           outw();
+
+static Bit16u         get_SS();
+
+// Output
+static void           printf();
+static void           unimplemented();
+static void           unknown();
+
+static Bit8u find_vga_entry();
+
+static void memsetb();
+static void memsetw();
+static void memcpyb();
+static void memcpyw();
+
+static void biosfn_set_video_mode();
+static void biosfn_set_cursor_shape();
+static void biosfn_set_cursor_pos();
+static void biosfn_get_cursor_pos();
+static void biosfn_set_active_page();
+static void biosfn_scroll();
+static void biosfn_read_char_attr();
+static void biosfn_write_char_attr();
+static void biosfn_write_char_only();
+static void biosfn_write_pixel();
+static void biosfn_read_pixel();
+static void biosfn_write_teletype();
+static void biosfn_perform_gray_scale_summing();
+static void biosfn_load_text_user_pat();
+static void biosfn_load_text_8_14_pat();
+static void biosfn_load_text_8_8_pat();
+static void biosfn_load_text_8_16_pat();
+static void biosfn_load_gfx_8_8_chars();
+static void biosfn_load_gfx_user_chars();
+static void biosfn_load_gfx_8_14_chars();
+static void biosfn_load_gfx_8_8_dd_chars();
+static void biosfn_load_gfx_8_16_chars();
+static void biosfn_get_font_info();
+static void biosfn_alternate_prtsc();
+static void biosfn_switch_video_interface();
+static void biosfn_enable_video_refresh_control();
+static void biosfn_write_string();
+static void biosfn_read_state_info();
+static void biosfn_read_video_state_size();
+static Bit16u biosfn_save_video_state();
+static Bit16u biosfn_restore_video_state();
+extern Bit8u video_save_pointer_table[];
+
+// This is for compiling with gcc2 and gcc3
+#define ASM_START #asm
+#define ASM_END   #endasm
+
+ASM_START
+
+MACRO SET_INT_VECTOR
+  push ds
+  xor ax, ax
+  mov ds, ax
+  mov ax, ?3
+  mov ?1*4, ax
+  mov ax, ?2
+  mov ?1*4+2, ax
+  pop ds
+MEND
+
+ASM_END
+
+ASM_START
+.text
+.rom
+.org 0
+
+use16 386
+
+vgabios_start:
+.byte  0x55, 0xaa      /* BIOS signature, required for BIOS extensions */
+
+.byte  0x40            /* BIOS extension length in units of 512 bytes */
+
+
+vgabios_entry_point:
+           
+  jmp vgabios_init_func
+
+#ifdef PCIBIOS
+.org 0x18
+.word vgabios_pci_data
+#endif
+
+// Info from Bart Oldeman
+.org 0x1e
+.ascii  "IBM"
+.byte   0x00
+
+vgabios_name:
+.ascii "Plex86/Bochs VGABios"
+#ifdef PCIBIOS
+.ascii " (PCI)"
+#endif
+.ascii " "
+.byte  0x00
+
+vgabios_version:
+#ifndef VGABIOS_VERS
+.ascii "current-cvs"
+#else
+.ascii VGABIOS_VERS
+#endif
+.ascii " "
+
+vgabios_date:
+.ascii  VGABIOS_DATE
+.byte   0x0a,0x0d
+.byte  0x00
+
+vgabios_copyright:
+.ascii "(C) 2008 the LGPL VGABios developers Team"
+.byte  0x0a,0x0d
+.byte  0x00
+
+vgabios_license:
+.ascii "This VGA/VBE Bios is released under the GNU LGPL"
+.byte  0x0a,0x0d
+.byte  0x0a,0x0d
+.byte  0x00
+
+vgabios_website:
+.ascii "Please visit :"
+.byte  0x0a,0x0d
+;;.ascii  " . http://www.plex86.org"
+;;.byte        0x0a,0x0d
+.ascii " . http://bochs.sourceforge.net"
+.byte  0x0a,0x0d
+.ascii " . http://www.nongnu.org/vgabios"
+.byte  0x0a,0x0d
+.byte  0x0a,0x0d
+.byte  0x00
+
+#ifdef PCIBIOS
+vgabios_pci_data:
+.ascii "PCIR"
+#ifdef CIRRUS
+.word 0x1013
+.word 0x00b8 // CLGD5446
+#else
+#ifdef PCI_VID
+.word PCI_VID
+.word PCI_DID
+#else
+#error "Unknown PCI vendor and device id"
+#endif
+#endif
+.word 0 // reserved
+.word 0x18 // dlen
+.byte 0 // revision
+.byte 0x0 // class,hi: vga display
+.word 0x300 // class,lo: vga display
+.word 0x40 // bios size
+.word 1 // revision
+.byte 0 // intel x86 data
+.byte 0x80 // last image
+.word 0 // reserved
+#endif
+
+
+;; ============================================================================================
+;;
+;; Init Entry point
+;;
+;; ============================================================================================
+vgabios_init_func:
+
+;; init vga card
+  call init_vga_card
+
+;; init basic bios vars
+  call init_bios_area
+
+#ifdef VBE  
+;; init vbe functions
+  call vbe_init  
+#endif
+
+;; set int10 vect
+  SET_INT_VECTOR(0x10, #0xC000, #vgabios_int10_handler)
+
+#ifdef CIRRUS
+  call cirrus_init
+#endif
+
+;; display splash screen
+  call _display_splash_screen
+
+;; init video mode and clear the screen
+  mov ax,#0x0003
+  int #0x10
+
+;; show info
+  call _display_info
+
+#ifdef VBE  
+;; show vbe info
+  call vbe_display_info  
+#endif
+
+#ifdef CIRRUS
+;; show cirrus info
+  call cirrus_display_info
+#endif
+
+  retf
+ASM_END
+
+/*
+ *  int10 handled here
+ */
+ASM_START
+vgabios_int10_handler:
+  pushf
+#ifdef DEBUG
+  push es
+  push ds
+  pusha
+  mov   bx, #0xc000
+  mov   ds, bx
+  call _int10_debugmsg
+  popa
+  pop ds
+  pop es
+#endif
+  cmp   ah, #0x0f
+  jne   int10_test_1A
+  call  biosfn_get_video_mode
+  jmp   int10_end
+int10_test_1A:
+  cmp   ah, #0x1a
+  jne   int10_test_0B
+  call  biosfn_group_1A
+  jmp   int10_end
+int10_test_0B:
+  cmp   ah, #0x0b
+  jne   int10_test_1103
+  call  biosfn_group_0B
+  jmp   int10_end
+int10_test_1103:
+  cmp   ax, #0x1103
+  jne   int10_test_12
+  call  biosfn_set_text_block_specifier
+  jmp   int10_end
+int10_test_12:
+  cmp   ah, #0x12
+  jne   int10_test_101B
+  cmp   bl, #0x10
+  jne   int10_test_BL30
+  call  biosfn_get_ega_info
+  jmp   int10_end
+int10_test_BL30:
+  cmp   bl, #0x30
+  jne   int10_test_BL31
+  call  biosfn_select_vert_res
+  jmp   int10_end
+int10_test_BL31:
+  cmp   bl, #0x31
+  jne   int10_test_BL32
+  call  biosfn_enable_default_palette_loading
+  jmp   int10_end
+int10_test_BL32:
+  cmp   bl, #0x32
+  jne   int10_test_BL33
+  call  biosfn_enable_video_addressing
+  jmp   int10_end
+int10_test_BL33:
+  cmp   bl, #0x33
+  jne   int10_test_BL34
+  call  biosfn_enable_grayscale_summing
+  jmp   int10_end
+int10_test_BL34:
+  cmp   bl, #0x34
+  jne   int10_normal
+  call  biosfn_enable_cursor_emulation
+  jmp   int10_end
+int10_test_101B:
+  cmp   ax, #0x101b
+  je    int10_normal
+  cmp   ah, #0x10
+#ifndef VBE
+  jne   int10_normal
+#else
+  jne   int10_test_4F
+#endif
+  call  biosfn_group_10
+  jmp   int10_end
+#ifdef VBE
+int10_test_4F:
+  cmp   ah, #0x4f
+  jne   int10_normal
+  cmp   al, #0x03
+  jne   int10_test_vbe_05
+  call  vbe_biosfn_return_current_mode
+  jmp   int10_end
+int10_test_vbe_05:
+  cmp   al, #0x05
+  jne   int10_test_vbe_06
+  call  vbe_biosfn_display_window_control
+  jmp   int10_end
+int10_test_vbe_06:
+  cmp   al, #0x06
+  jne   int10_test_vbe_07
+  call  vbe_biosfn_set_get_logical_scan_line_length
+  jmp   int10_end
+int10_test_vbe_07:
+  cmp   al, #0x07
+  jne   int10_test_vbe_08
+  call  vbe_biosfn_set_get_display_start
+  jmp   int10_end
+int10_test_vbe_08:
+  cmp   al, #0x08
+  jne   int10_test_vbe_0A
+  call  vbe_biosfn_set_get_dac_palette_format
+  jmp   int10_end
+int10_test_vbe_0A:
+  cmp   al, #0x0A
+  jne   int10_normal
+  call  vbe_biosfn_return_protected_mode_interface
+  jmp   int10_end
+#endif
+
+int10_normal:
+  push es
+  push ds
+  pusha
+
+;; We have to set ds to access the right data segment
+  mov   bx, #0xc000
+  mov   ds, bx
+  call _int10_func
+
+  popa
+  pop ds
+  pop es
+int10_end:
+  popf
+  iret
+ASM_END
+
+#include "vgatables.h"
+#include "vgafonts.h"
+
+/*
+ * Boot time harware inits 
+ */
+ASM_START
+init_vga_card:
+;; switch to color mode and enable CPU access 480 lines
+  mov dx, #0x3C2
+  mov al, #0xC3
+  outb dx,al
+
+;; more than 64k 3C4/04
+  mov dx, #0x3C4
+  mov al, #0x04
+  outb dx,al
+  mov dx, #0x3C5
+  mov al, #0x02
+  outb dx,al
+
+#if defined(USE_BX_INFO) || defined(DEBUG)
+  mov  bx, #msg_vga_init
+  push bx
+  call _printf
+#endif
+  inc  sp
+  inc  sp
+  ret
+
+#if defined(USE_BX_INFO) || defined(DEBUG)
+msg_vga_init:
+.ascii "VGABios $Id$"
+.byte 0x0d,0x0a,0x00
+#endif
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+/*
+ *  Boot time bios area inits 
+ */
+ASM_START
+init_bios_area:
+  push  ds
+  mov   ax, # BIOSMEM_SEG
+  mov   ds, ax
+
+;; init detected hardware BIOS Area
+  mov   bx, # BIOSMEM_INITIAL_MODE
+  mov   ax, [bx]
+  and   ax, #0xffcf
+;; set 80x25 color (not clear from RBIL but usual)
+  or    ax, #0x0020
+  mov   [bx], ax
+
+;; Just for the first int10 find its children
+
+;; the default char height
+  mov   bx, # BIOSMEM_CHAR_HEIGHT
+  mov   al, #0x10
+  mov   [bx], al
+
+;; Clear the screen 
+  mov   bx, # BIOSMEM_VIDEO_CTL
+  mov   al, #0x60
+  mov   [bx], al
+
+;; Set the basic screen we have
+  mov   bx, # BIOSMEM_SWITCHES
+  mov   al, #0xf9
+  mov   [bx], al
+
+;; Set the basic modeset options
+  mov   bx, # BIOSMEM_MODESET_CTL
+  mov   al, #0x51
+  mov   [bx], al
+
+;; Set the  default MSR
+  mov   bx, # BIOSMEM_CURRENT_MSR
+  mov   al, #0x09
+  mov   [bx], al
+
+  pop ds
+  ret
+
+_video_save_pointer_table:
+  .word _video_param_table
+  .word 0xc000
+
+  .word 0 /* XXX: fill it */
+  .word 0
+
+  .word 0 /* XXX: fill it */
+  .word 0
+
+  .word 0 /* XXX: fill it */
+  .word 0
+
+  .word 0 /* XXX: fill it */
+  .word 0
+
+  .word 0 /* XXX: fill it */
+  .word 0
+
+  .word 0 /* XXX: fill it */
+  .word 0
+
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+/*
+ *  Boot time Splash screen
+ */
+static void display_splash_screen()
+{
+}
+
+// --------------------------------------------------------------------------------------------
+/*
+ *  Tell who we are
+ */
+
+static void display_info()
+{
+ASM_START
+ mov ax,#0xc000
+ mov ds,ax
+ mov si,#vgabios_name
+ call _display_string
+ mov si,#vgabios_version
+ call _display_string
+ ;;mov si,#vgabios_copyright
+ ;;call _display_string
+ ;;mov si,#crlf
+ ;;call _display_string
+
+ mov si,#vgabios_license
+ call _display_string
+ mov si,#vgabios_website
+ call _display_string
+ASM_END
+}
+
+static void display_string()
+{
+ // Get length of string
+ASM_START
+ mov ax,ds
+ mov es,ax
+ mov di,si
+ xor cx,cx
+ not cx
+ xor al,al
+ cld
+ repne 
+  scasb
+ not cx
+ dec cx
+ push cx
+
+ mov ax,#0x0300
+ mov bx,#0x0000
+ int #0x10
+ pop cx
+ mov ax,#0x1301
+ mov bx,#0x000b
+ mov bp,si
+ int #0x10
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+#ifdef DEBUG
+static void int10_debugmsg(DI, SI, BP, SP, BX, DX, CX, AX, DS, ES, FLAGS)
+  Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, DS, FLAGS;
+{
+ // 0E is write char...
+ if(GET_AH()!=0x0E)
+  printf("vgabios call ah%02x al%02x bx%04x cx%04x dx%04x\n",GET_AH(),GET_AL(),BX,CX,DX);
+}
+#endif
+
+// --------------------------------------------------------------------------------------------
+/*
+ * int10 main dispatcher
+ */
+static void int10_func(DI, SI, BP, SP, BX, DX, CX, AX, DS, ES, FLAGS)
+  Bit16u DI, SI, BP, SP, BX, DX, CX, AX, ES, DS, FLAGS;
+{
+
+ // BIOS functions
+ switch(GET_AH())
+  {
+   case 0x00:
+     biosfn_set_video_mode(GET_AL());
+     switch(GET_AL()&0x7F)
+      {case 6: 
+        SET_AL(0x3F);
+        break;
+       case 0:
+       case 1:
+       case 2:
+       case 3:
+       case 4:
+       case 5:
+       case 7:
+        SET_AL(0x30);
+        break;
+      default:
+        SET_AL(0x20);
+      }
+     break;
+   case 0x01:
+     biosfn_set_cursor_shape(GET_CH(),GET_CL());
+     break;
+   case 0x02:
+     biosfn_set_cursor_pos(GET_BH(),DX);
+     break;
+   case 0x03:
+     biosfn_get_cursor_pos(GET_BH(),&CX,&DX);
+     break;
+   case 0x04:
+     // Read light pen pos (unimplemented)
+#ifdef DEBUG
+     unimplemented();
+#endif
+     AX=0x00;
+     BX=0x00;
+     CX=0x00;
+     DX=0x00;
+     break;
+   case 0x05:
+     biosfn_set_active_page(GET_AL());
+     break;
+   case 0x06:
+     biosfn_scroll(GET_AL(),GET_BH(),GET_CH(),GET_CL(),GET_DH(),GET_DL(),0xFF,SCROLL_UP);
+     break;
+   case 0x07:
+     biosfn_scroll(GET_AL(),GET_BH(),GET_CH(),GET_CL(),GET_DH(),GET_DL(),0xFF,SCROLL_DOWN);
+     break;
+   case 0x08:
+     biosfn_read_char_attr(GET_BH(),&AX);
+     break;
+   case 0x09:
+     biosfn_write_char_attr(GET_AL(),GET_BH(),GET_BL(),CX);
+     break;
+   case 0x0A:
+     biosfn_write_char_only(GET_AL(),GET_BH(),GET_BL(),CX);
+     break;
+   case 0x0C:
+     biosfn_write_pixel(GET_BH(),GET_AL(),CX,DX);
+     break;
+   case 0x0D:
+     biosfn_read_pixel(GET_BH(),CX,DX,&AX);
+     break;
+   case 0x0E:
+     // Ralf Brown Interrupt list is WRONG on bh(page)
+     // We do output only on the current page !
+     biosfn_write_teletype(GET_AL(),0xff,GET_BL(),NO_ATTR);
+     break;
+   case 0x10:
+     // All other functions of group AH=0x10 rewritten in assembler
+     biosfn_perform_gray_scale_summing(BX,CX);
+     break;
+   case 0x11:
+     switch(GET_AL())
+      {
+       case 0x00:
+       case 0x10:
+        biosfn_load_text_user_pat(GET_AL(),ES,BP,CX,DX,GET_BL(),GET_BH());
+        break;
+       case 0x01:
+       case 0x11:
+        biosfn_load_text_8_14_pat(GET_AL(),GET_BL());
+        break;
+       case 0x02:
+       case 0x12:
+        biosfn_load_text_8_8_pat(GET_AL(),GET_BL());
+        break;
+       case 0x04:
+       case 0x14:
+        biosfn_load_text_8_16_pat(GET_AL(),GET_BL());
+        break;
+       case 0x20:
+        biosfn_load_gfx_8_8_chars(ES,BP);
+        break;
+       case 0x21:
+        biosfn_load_gfx_user_chars(ES,BP,CX,GET_BL(),GET_DL());
+        break;
+       case 0x22:
+        biosfn_load_gfx_8_14_chars(GET_BL());
+        break;
+       case 0x23:
+        biosfn_load_gfx_8_8_dd_chars(GET_BL());
+        break;
+       case 0x24:
+        biosfn_load_gfx_8_16_chars(GET_BL());
+        break;
+       case 0x30:
+        biosfn_get_font_info(GET_BH(),&ES,&BP,&CX,&DX);
+        break;
+#ifdef DEBUG
+       default:
+        unknown();
+#endif
+      }
+     
+     break;
+   case 0x12:
+     switch(GET_BL())
+      {
+       case 0x20:
+        biosfn_alternate_prtsc();
+        break;
+       case 0x35:
+        biosfn_switch_video_interface(GET_AL(),ES,DX);
+        SET_AL(0x12);
+        break;
+       case 0x36:
+        biosfn_enable_video_refresh_control(GET_AL());
+        SET_AL(0x12);
+        break;
+#ifdef DEBUG
+       default:
+        unknown();
+#endif
+      }
+     break;
+   case 0x13:
+     biosfn_write_string(GET_AL(),GET_BH(),GET_BL(),CX,GET_DH(),GET_DL(),ES,BP);
+     break;
+   case 0x1B:
+     biosfn_read_state_info(BX,ES,DI);
+     SET_AL(0x1B);
+     break;
+   case 0x1C:
+     switch(GET_AL())
+      {
+       case 0x00:
+        biosfn_read_video_state_size(CX,&BX);
+        break;
+       case 0x01:
+        biosfn_save_video_state(CX,ES,BX);
+        break;
+       case 0x02:
+        biosfn_restore_video_state(CX,ES,BX);
+        break;
+#ifdef DEBUG
+       default:
+        unknown();
+#endif
+      }
+     SET_AL(0x1C);
+     break;
+
+#ifdef VBE 
+   case 0x4f:
+     if (vbe_has_vbe_display()) {
+       switch(GET_AL())
+       {
+         case 0x00:
+          vbe_biosfn_return_controller_information(&AX,ES,DI);
+          break;
+         case 0x01:
+          vbe_biosfn_return_mode_information(&AX,CX,ES,DI);
+          break;
+         case 0x02:
+          vbe_biosfn_set_mode(&AX,BX,ES,DI);
+          break;
+         case 0x04:
+          vbe_biosfn_save_restore_state(&AX, CX, DX, ES, &BX);
+          break;
+         case 0x09:
+          //FIXME
+#ifdef DEBUG
+          unimplemented();
+#endif
+          // function failed
+          AX=0x100;
+          break;
+         case 0x0A:
+          //FIXME
+#ifdef DEBUG
+          unimplemented();
+#endif
+          // function failed
+          AX=0x100;
+          break;
+         default:
+#ifdef DEBUG
+          unknown();
+#endif                  
+          // function failed
+          AX=0x100;
+          }
+        }
+        else {
+          // No VBE display
+          AX=0x0100;
+          }
+        break;
+#endif
+
+#ifdef DEBUG
+   default:
+     unknown();
+#endif
+  }
+}
+
+// ============================================================================================
+// 
+// BIOS functions
+// 
+// ============================================================================================
+
+static void biosfn_set_video_mode(mode) Bit8u mode; 
+{// mode: Bit 7 is 1 if no clear screen
+
+ // Should we clear the screen ?
+ Bit8u noclearmem=mode&0x80;
+ Bit8u line,mmask,*palette,vpti;
+ Bit16u i,twidth,theightm1,cheight;
+ Bit8u modeset_ctl,video_ctl,vga_switches;
+ Bit16u crtc_addr;
+#ifdef VBE
+ if (vbe_has_vbe_display()) { 
+   dispi_set_enable(VBE_DISPI_DISABLED);
+  }
+#endif // def VBE
+ // The real mode
+ mode=mode&0x7f;
+
+ // find the entry in the video modes
+ line=find_vga_entry(mode);
+
+#ifdef DEBUG
+ printf("mode search %02x found line %02x\n",mode,line);
+#endif
+
+ if(line==0xFF)
+  return;
+
+ vpti=line_to_vpti[line];
+ twidth=video_param_table[vpti].twidth;
+ theightm1=video_param_table[vpti].theightm1;
+ cheight=video_param_table[vpti].cheight;
+ // Read the bios vga control
+ video_ctl=read_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL);
+
+ // Read the bios vga switches
+ vga_switches=read_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES);
+
+ // Read the bios mode set control
+ modeset_ctl=read_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL);
+
+ // Then we know the number of lines
+// FIXME
+
+ // if palette loading (bit 3 of modeset ctl = 0)
+ if((modeset_ctl&0x08)==0)
+  {// Set the PEL mask
+   outb(VGAREG_PEL_MASK,vga_modes[line].pelmask);
+
+   // Set the whole dac always, from 0
+   outb(VGAREG_DAC_WRITE_ADDRESS,0x00);
+
+   // From which palette
+   switch(vga_modes[line].dacmodel)
+    {case 0:
+      palette=&palette0;
+      break;
+     case 1:
+      palette=&palette1;
+      break;
+     case 2:
+      palette=&palette2;
+      break;
+     case 3:
+      palette=&palette3;
+      break;
+    }
+   // Always 256*3 values
+   for(i=0;i<0x0100;i++)
+    {if(i<=dac_regs[vga_modes[line].dacmodel])
+      {outb(VGAREG_DAC_DATA,palette[(i*3)+0]);
+       outb(VGAREG_DAC_DATA,palette[(i*3)+1]);
+       outb(VGAREG_DAC_DATA,palette[(i*3)+2]);
+      }
+     else
+      {outb(VGAREG_DAC_DATA,0);
+       outb(VGAREG_DAC_DATA,0);
+       outb(VGAREG_DAC_DATA,0);
+      }
+    }
+   if((modeset_ctl&0x02)==0x02)
+    {
+     biosfn_perform_gray_scale_summing(0x00, 0x100);
+    }
+  }
+
+ // Reset Attribute Ctl flip-flop
+ inb(VGAREG_ACTL_RESET);
+
+ // Set Attribute Ctl
+ for(i=0;i<=0x13;i++)
+  {outb(VGAREG_ACTL_ADDRESS,i);
+   outb(VGAREG_ACTL_WRITE_DATA,video_param_table[vpti].actl_regs[i]);
+  }
+ outb(VGAREG_ACTL_ADDRESS,0x14);
+ outb(VGAREG_ACTL_WRITE_DATA,0x00);
+
+ // Set Sequencer Ctl
+ outb(VGAREG_SEQU_ADDRESS,0);
+ outb(VGAREG_SEQU_DATA,0x03);
+ for(i=1;i<=4;i++)
+  {outb(VGAREG_SEQU_ADDRESS,i);
+   outb(VGAREG_SEQU_DATA,video_param_table[vpti].sequ_regs[i - 1]);
+  }
+
+ // Set Grafx Ctl
+ for(i=0;i<=8;i++)
+  {outb(VGAREG_GRDC_ADDRESS,i);
+   outb(VGAREG_GRDC_DATA,video_param_table[vpti].grdc_regs[i]);
+  }
+
+ // Set CRTC address VGA or MDA 
+ crtc_addr=vga_modes[line].memmodel==MTEXT?VGAREG_MDA_CRTC_ADDRESS:VGAREG_VGA_CRTC_ADDRESS;
+
+ // Disable CRTC write protection
+ outw(crtc_addr,0x0011);
+ // Set CRTC regs
+ for(i=0;i<=0x18;i++)
+  {outb(crtc_addr,i);
+   outb(crtc_addr+1,video_param_table[vpti].crtc_regs[i]);
+  }
+
+ // Set the misc register
+ outb(VGAREG_WRITE_MISC_OUTPUT,video_param_table[vpti].miscreg);
+
+ // Enable video
+ outb(VGAREG_ACTL_ADDRESS,0x20);
+ inb(VGAREG_ACTL_RESET);
+
+ if(noclearmem==0x00)
+  {
+   if(vga_modes[line].class==TEXT)
+    {
+     memsetw(vga_modes[line].sstart,0,0x0720,0x4000); // 32k
+    }
+   else
+    {
+     if(mode<0x0d)
+      {
+       memsetw(vga_modes[line].sstart,0,0x0000,0x4000); // 32k
+      }
+     else
+      {
+       outb( VGAREG_SEQU_ADDRESS, 0x02 );
+       mmask = inb( VGAREG_SEQU_DATA );
+       outb( VGAREG_SEQU_DATA, 0x0f ); // all planes
+       memsetw(vga_modes[line].sstart,0,0x0000,0x8000); // 64k
+       outb( VGAREG_SEQU_DATA, mmask );
+      }
+    }
+  }
+
+ // Set the BIOS mem
+ write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE,mode);
+ write_word(BIOSMEM_SEG,BIOSMEM_NB_COLS,twidth);
+ write_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE,*(Bit16u *)&video_param_table[vpti].slength_l);
+ write_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS,crtc_addr);
+ write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS,theightm1);
+ write_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT,cheight);
+ write_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL,(0x60|noclearmem));
+ write_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES,0xF9);
+ write_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL,read_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL)&0x7f);
+
+ // FIXME We nearly have the good tables. to be reworked
+ write_byte(BIOSMEM_SEG,BIOSMEM_DCC_INDEX,0x08);    // 8 is VGA should be ok for now
+ write_word(BIOSMEM_SEG,BIOSMEM_VS_POINTER, video_save_pointer_table);
+ write_word(BIOSMEM_SEG,BIOSMEM_VS_POINTER+2, 0xc000);
+
+ // FIXME
+ write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MSR,0x00); // Unavailable on vanilla vga, but...
+ write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAL,0x00); // Unavailable on vanilla vga, but...
+ // Set cursor shape
+ if(vga_modes[line].class==TEXT)
+  {
+   biosfn_set_cursor_shape(0x06,0x07);
+  }
+
+ // Set cursor pos for page 0..7
+ for(i=0;i<8;i++)
+  biosfn_set_cursor_pos(i,0x0000);
+
+ // Set active page 0
+ biosfn_set_active_page(0x00);
+
+ // Write the fonts in memory
+ if(vga_modes[line].class==TEXT)
+  { 
+ASM_START
+  ;; copy and activate 8x16 font
+  mov ax, #0x1104
+  mov bl, #0x00
+  int #0x10
+  mov ax, #0x1103
+  mov bl, #0x00
+  int #0x10
+ASM_END
+  }
+
+ // Set the ints 0x1F and 0x43
+ASM_START
+ SET_INT_VECTOR(0x1f, #0xC000, #_vgafont8+128*8)
+ASM_END
+
+  switch(cheight)
+   {case 8:
+ASM_START
+     SET_INT_VECTOR(0x43, #0xC000, #_vgafont8)
+ASM_END
+     break;
+    case 14:
+ASM_START
+     SET_INT_VECTOR(0x43, #0xC000, #_vgafont14)
+ASM_END
+     break;
+    case 16:
+ASM_START
+     SET_INT_VECTOR(0x43, #0xC000, #_vgafont16)
+ASM_END
+     break;
+   }
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_set_cursor_shape (CH,CL) 
+Bit8u CH;Bit8u CL; 
+{Bit16u cheight,curs,crtc_addr;
+ Bit8u modeset_ctl;
+
+ CH&=0x3f;
+ CL&=0x1f;
+
+ curs=(CH<<8)+CL;
+ write_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE,curs);
+
+ modeset_ctl=read_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL);
+ cheight = read_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT);
+ if((modeset_ctl&0x01) && (cheight>8) && (CL<8) && (CH<0x20))
+  {
+   if(CL!=(CH+1))
+    {
+     CH = ((CH+1) * cheight / 8) -1;
+    }
+   else
+    {
+     CH = ((CL+1) * cheight / 8) - 2;
+    }
+   CL = ((CL+1) * cheight / 8) - 1;
+  }
+
+ // CTRC regs 0x0a and 0x0b
+ crtc_addr=read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+ outb(crtc_addr,0x0a);
+ outb(crtc_addr+1,CH);
+ outb(crtc_addr,0x0b);
+ outb(crtc_addr+1,CL);
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_set_cursor_pos (page, cursor) 
+Bit8u page;Bit16u cursor;
+{
+ Bit8u xcurs,ycurs,current;
+ Bit16u nbcols,nbrows,address,crtc_addr;
+
+ // Should not happen...
+ if(page>7)return;
+
+ // Bios cursor pos
+ write_word(BIOSMEM_SEG, BIOSMEM_CURSOR_POS+2*page, cursor);
+
+ // Set the hardware cursor
+ current=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
+ if(page==current)
+  {
+   // Get the dimensions
+   nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+   nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
+
+   xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8;
+   // Calculate the address knowing nbcols nbrows and page num
+   address=SCREEN_IO_START(nbcols,nbrows,page)+xcurs+ycurs*nbcols;
+   
+   // CRTC regs 0x0e and 0x0f
+   crtc_addr=read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+   outb(crtc_addr,0x0e);
+   outb(crtc_addr+1,(address&0xff00)>>8);
+   outb(crtc_addr,0x0f);
+   outb(crtc_addr+1,address&0x00ff);
+  }
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_get_cursor_pos (page,shape, pos) 
+Bit8u page;Bit16u *shape;Bit16u *pos;
+{
+ Bit16u ss=get_SS();
+
+ // Default
+ write_word(ss, shape, 0);
+ write_word(ss, pos, 0);
+
+ if(page>7)return;
+ // FIXME should handle VGA 14/16 lines
+ write_word(ss,shape,read_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE));
+ write_word(ss,pos,read_word(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2));
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_set_active_page (page) 
+Bit8u page;
+{
+ Bit16u cursor,dummy,crtc_addr;
+ Bit16u nbcols,nbrows,address;
+ Bit8u mode,line;
+
+ if(page>7)return;
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+
+ // Get pos curs pos for the right page 
+ biosfn_get_cursor_pos(page,&dummy,&cursor);
+
+ if(vga_modes[line].class==TEXT)
+  {
+   // Get the dimensions
+   nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+   nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
+   // Calculate the address knowing nbcols nbrows and page num
+   address=SCREEN_MEM_START(nbcols,nbrows,page);
+   write_word(BIOSMEM_SEG,BIOSMEM_CURRENT_START,address);
+
+   // Start address
+   address=SCREEN_IO_START(nbcols,nbrows,page);
+  }
+ else
+  {
+   address = page * (*(Bit16u *)&video_param_table[line_to_vpti[line]].slength_l);
+  }
+
+ // CRTC regs 0x0c and 0x0d
+ crtc_addr=read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+ outb(crtc_addr,0x0c);
+ outb(crtc_addr+1,(address&0xff00)>>8);
+ outb(crtc_addr,0x0d);
+ outb(crtc_addr+1,address&0x00ff);
+
+ // And change the BIOS page
+ write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE,page);
+
+#ifdef DEBUG
+ printf("Set active page %02x address %04x\n",page,address);
+#endif
+
+ // Display the cursor, now the page is active
+ biosfn_set_cursor_pos(page,cursor);
+}
+
+// --------------------------------------------------------------------------------------------
+static void vgamem_copy_pl4(xstart,ysrc,ydest,cols,nbcols,cheight)
+Bit8u xstart;Bit8u ysrc;Bit8u ydest;Bit8u cols;Bit8u nbcols;Bit8u cheight;
+{
+ Bit16u src,dest;
+ Bit8u i;
+
+ src=ysrc*cheight*nbcols+xstart;
+ dest=ydest*cheight*nbcols+xstart;
+ outw(VGAREG_GRDC_ADDRESS, 0x0105);
+ for(i=0;i<cheight;i++)
+  {
+   memcpyb(0xa000,dest+i*nbcols,0xa000,src+i*nbcols,cols);
+  }
+ outw(VGAREG_GRDC_ADDRESS, 0x0005);
+}
+
+// --------------------------------------------------------------------------------------------
+static void vgamem_fill_pl4(xstart,ystart,cols,nbcols,cheight,attr)
+Bit8u xstart;Bit8u ystart;Bit8u cols;Bit8u nbcols;Bit8u cheight;Bit8u attr;
+{
+ Bit16u dest;
+ Bit8u i;
+
+ dest=ystart*cheight*nbcols+xstart;
+ outw(VGAREG_GRDC_ADDRESS, 0x0205);
+ for(i=0;i<cheight;i++)
+  {
+   memsetb(0xa000,dest+i*nbcols,attr,cols);
+  }
+ outw(VGAREG_GRDC_ADDRESS, 0x0005);
+}
+
+// --------------------------------------------------------------------------------------------
+static void vgamem_copy_cga(xstart,ysrc,ydest,cols,nbcols,cheight)
+Bit8u xstart;Bit8u ysrc;Bit8u ydest;Bit8u cols;Bit8u nbcols;Bit8u cheight;
+{
+ Bit16u src,dest;
+ Bit8u i;
+
+ src=((ysrc*cheight*nbcols)>>1)+xstart;
+ dest=((ydest*cheight*nbcols)>>1)+xstart;
+ for(i=0;i<cheight;i++)
+  {
+   if (i & 1)
+     memcpyb(0xb800,0x2000+dest+(i>>1)*nbcols,0xb800,0x2000+src+(i>>1)*nbcols,cols);
+   else
+     memcpyb(0xb800,dest+(i>>1)*nbcols,0xb800,src+(i>>1)*nbcols,cols);
+  }
+}
+
+// --------------------------------------------------------------------------------------------
+static void vgamem_fill_cga(xstart,ystart,cols,nbcols,cheight,attr)
+Bit8u xstart;Bit8u ystart;Bit8u cols;Bit8u nbcols;Bit8u cheight;Bit8u attr;
+{
+ Bit16u dest;
+ Bit8u i;
+
+ dest=((ystart*cheight*nbcols)>>1)+xstart;
+ for(i=0;i<cheight;i++)
+  {
+   if (i & 1)
+     memsetb(0xb800,0x2000+dest+(i>>1)*nbcols,attr,cols);
+   else
+     memsetb(0xb800,dest+(i>>1)*nbcols,attr,cols);
+  }
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_scroll (nblines,attr,rul,cul,rlr,clr,page,dir)
+Bit8u nblines;Bit8u attr;Bit8u rul;Bit8u cul;Bit8u rlr;Bit8u clr;Bit8u page;Bit8u dir;
+{
+ // page == 0xFF if current
+
+ Bit8u mode,line,cheight,bpp,cols;
+ Bit16u nbcols,nbrows,i;
+ Bit16u address;
+
+ if(rul>rlr)return;
+ if(cul>clr)return;
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+
+ // Get the dimensions
+ nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
+ nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+
+ // Get the current page
+ if(page==0xFF)
+  page=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
+
+ if(rlr>=nbrows)rlr=nbrows-1;
+ if(clr>=nbcols)clr=nbcols-1;
+ if(nblines>nbrows)nblines=0;
+ cols=clr-cul+1;
+
+ if(vga_modes[line].class==TEXT)
+  {
+   // Compute the address
+   address=SCREEN_MEM_START(nbcols,nbrows,page);
+#ifdef DEBUG
+   printf("Scroll, address %04x (%04x %04x %02x)\n",address,nbrows,nbcols,page);
+#endif
+
+   if(nblines==0&&rul==0&&cul==0&&rlr==nbrows-1&&clr==nbcols-1)
+    {
+     memsetw(vga_modes[line].sstart,address,(Bit16u)attr*0x100+' ',nbrows*nbcols);
+    }
+   else
+    {// if Scroll up
+     if(dir==SCROLL_UP)
+      {for(i=rul;i<=rlr;i++)
+        {
+         if((i+nblines>rlr)||(nblines==0))
+          memsetw(vga_modes[line].sstart,address+(i*nbcols+cul)*2,(Bit16u)attr*0x100+' ',cols);
+         else
+          memcpyw(vga_modes[line].sstart,address+(i*nbcols+cul)*2,vga_modes[line].sstart,((i+nblines)*nbcols+cul)*2,cols);
+        }
+      }
+     else
+      {for(i=rlr;i>=rul;i--)
+        {
+         if((i<rul+nblines)||(nblines==0))
+          memsetw(vga_modes[line].sstart,address+(i*nbcols+cul)*2,(Bit16u)attr*0x100+' ',cols);
+         else
+          memcpyw(vga_modes[line].sstart,address+(i*nbcols+cul)*2,vga_modes[line].sstart,((i-nblines)*nbcols+cul)*2,cols);
+         if (i>rlr) break;
+        }
+      }
+    }
+  }
+ else
+  {
+   // FIXME gfx mode not complete
+   cheight=video_param_table[line_to_vpti[line]].cheight;
+   switch(vga_modes[line].memmodel)
+    {
+     case PLANAR4:
+     case PLANAR1:
+       if(nblines==0&&rul==0&&cul==0&&rlr==nbrows-1&&clr==nbcols-1)
+        {
+         outw(VGAREG_GRDC_ADDRESS, 0x0205);
+         memsetb(vga_modes[line].sstart,0,attr,nbrows*nbcols*cheight);
+         outw(VGAREG_GRDC_ADDRESS, 0x0005);
+        }
+       else
+        {// if Scroll up
+         if(dir==SCROLL_UP)
+          {for(i=rul;i<=rlr;i++)
+            {
+             if((i+nblines>rlr)||(nblines==0))
+              vgamem_fill_pl4(cul,i,cols,nbcols,cheight,attr);
+             else
+              vgamem_copy_pl4(cul,i+nblines,i,cols,nbcols,cheight);
+            }
+          }
+         else
+          {for(i=rlr;i>=rul;i--)
+            {
+             if((i<rul+nblines)||(nblines==0))
+              vgamem_fill_pl4(cul,i,cols,nbcols,cheight,attr);
+             else
+              vgamem_copy_pl4(cul,i,i-nblines,cols,nbcols,cheight);
+             if (i>rlr) break;
+            }
+          }
+        }
+       break;
+     case CGA:
+       bpp=vga_modes[line].pixbits;
+       if(nblines==0&&rul==0&&cul==0&&rlr==nbrows-1&&clr==nbcols-1)
+        {
+         memsetb(vga_modes[line].sstart,0,attr,nbrows*nbcols*cheight*bpp);
+        }
+       else
+        {
+         if(bpp==2)
+          {
+           cul<<=1;
+           cols<<=1;
+           nbcols<<=1;
+          }
+         // if Scroll up
+         if(dir==SCROLL_UP)
+          {for(i=rul;i<=rlr;i++)
+            {
+             if((i+nblines>rlr)||(nblines==0))
+              vgamem_fill_cga(cul,i,cols,nbcols,cheight,attr);
+             else
+              vgamem_copy_cga(cul,i+nblines,i,cols,nbcols,cheight);
+            }
+          }
+         else
+          {for(i=rlr;i>=rul;i--)
+            {
+             if((i<rul+nblines)||(nblines==0))
+              vgamem_fill_cga(cul,i,cols,nbcols,cheight,attr);
+             else
+              vgamem_copy_cga(cul,i,i-nblines,cols,nbcols,cheight);
+             if (i>rlr) break;
+            }
+          }
+        }
+       break;
+#ifdef DEBUG
+     default:
+       printf("Scroll in graphics mode ");
+       unimplemented();
+#endif
+    }
+  }
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_read_char_attr (page,car) 
+Bit8u page;Bit16u *car;
+{Bit16u ss=get_SS();
+ Bit8u xcurs,ycurs,mode,line;
+ Bit16u nbcols,nbrows,address;
+ Bit16u cursor,dummy;
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+
+ // Get the cursor pos for the page
+ biosfn_get_cursor_pos(page,&dummy,&cursor);
+ xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8;
+
+ // Get the dimensions
+ nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
+ nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+
+ if(vga_modes[line].class==TEXT)
+  {
+   // Compute the address
+   address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2;
+
+   write_word(ss,car,read_word(vga_modes[line].sstart,address));
+  }
+ else
+  {
+   // FIXME gfx mode
+#ifdef DEBUG
+   unimplemented();
+#endif
+  }
+}
+
+// --------------------------------------------------------------------------------------------
+static void write_gfx_char_pl4(car,attr,xcurs,ycurs,nbcols,cheight)
+Bit8u car;Bit8u attr;Bit8u xcurs;Bit8u ycurs;Bit8u nbcols;Bit8u cheight;
+{
+ Bit8u i,j,mask;
+ Bit8u *fdata;
+ Bit16u addr,dest,src;
+
+ switch(cheight)
+  {case 14:
+    fdata = &vgafont14;
+    break;
+   case 16:
+    fdata = &vgafont16;
+    break;
+   default:
+    fdata = &vgafont8;
+  }
+ addr=xcurs+ycurs*cheight*nbcols;
+ src = car * cheight;
+ outw(VGAREG_SEQU_ADDRESS, 0x0f02);
+ outw(VGAREG_GRDC_ADDRESS, 0x0205);
+ if(attr&0x80)
+  {
+   outw(VGAREG_GRDC_ADDRESS, 0x1803);
+  }
+ else
+  {
+   outw(VGAREG_GRDC_ADDRESS, 0x0003);
+  }
+ for(i=0;i<cheight;i++)
+  {
+   dest=addr+i*nbcols;
+   for(j=0;j<8;j++)
+    {
+     mask=0x80>>j;
+     outw(VGAREG_GRDC_ADDRESS, (mask << 8) | 0x08);
+     read_byte(0xa000,dest);
+     if(fdata[src+i]&mask)
+      {
+       write_byte(0xa000,dest,attr&0x0f);
+      }
+     else
+      {
+       write_byte(0xa000,dest,0x00);
+      }
+    }
+  }
+ASM_START
+  mov dx, # VGAREG_GRDC_ADDRESS
+  mov ax, #0xff08
+  out dx, ax
+  mov ax, #0x0005
+  out dx, ax
+  mov ax, #0x0003
+  out dx, ax
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+static void write_gfx_char_cga(car,attr,xcurs,ycurs,nbcols,bpp)
+Bit8u car;Bit8u attr;Bit8u xcurs;Bit8u ycurs;Bit8u nbcols;Bit8u bpp;
+{
+ Bit8u i,j,mask,data;
+ Bit8u *fdata;
+ Bit16u addr,dest,src;
+
+ fdata = &vgafont8;
+ addr=(xcurs*bpp)+ycurs*320;
+ src = car * 8;
+ for(i=0;i<8;i++)
+  {
+   dest=addr+(i>>1)*80;
+   if (i & 1) dest += 0x2000;
+   mask = 0x80;
+   if (bpp == 1)
+    {
+     if (attr & 0x80)
+      {
+       data = read_byte(0xb800,dest);
+      }
+     else
+      {
+       data = 0x00;
+      }
+     for(j=0;j<8;j++)
+      {
+       if (fdata[src+i] & mask)
+        {
+         if (attr & 0x80)
+          {
+           data ^= (attr & 0x01) << (7-j);
+          }
+         else
+          {
+           data |= (attr & 0x01) << (7-j);
+          }
+        }
+       mask >>= 1;
+      }
+     write_byte(0xb800,dest,data);
+    }
+   else
+    {
+     while (mask > 0)
+      {
+       if (attr & 0x80)
+        {
+         data = read_byte(0xb800,dest);
+        }
+       else
+        {
+         data = 0x00;
+        }
+       for(j=0;j<4;j++)
+        {
+         if (fdata[src+i] & mask)
+          {
+           if (attr & 0x80)
+            {
+             data ^= (attr & 0x03) << ((3-j)*2);
+            }
+           else
+            {
+             data |= (attr & 0x03) << ((3-j)*2);
+            }
+          }
+         mask >>= 1;
+        }
+       write_byte(0xb800,dest,data);
+       dest += 1;
+      }
+    }
+  }
+}
+
+// --------------------------------------------------------------------------------------------
+static void write_gfx_char_lin(car,attr,xcurs,ycurs,nbcols)
+Bit8u car;Bit8u attr;Bit8u xcurs;Bit8u ycurs;Bit8u nbcols;
+{
+ Bit8u i,j,mask,data;
+ Bit8u *fdata;
+ Bit16u addr,dest,src;
+
+ fdata = &vgafont8;
+ addr=xcurs*8+ycurs*nbcols*64;
+ src = car * 8;
+ for(i=0;i<8;i++)
+  {
+   dest=addr+i*nbcols*8;
+   mask = 0x80;
+   for(j=0;j<8;j++)
+    {
+     data = 0x00;
+     if (fdata[src+i] & mask)
+      {
+       data = attr;
+      }
+     write_byte(0xa000,dest+j,data);
+     mask >>= 1;
+    }
+  }
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_write_char_attr (car,page,attr,count) 
+Bit8u car;Bit8u page;Bit8u attr;Bit16u count;
+{
+ Bit8u cheight,xcurs,ycurs,mode,line,bpp;
+ Bit16u nbcols,nbrows,address;
+ Bit16u cursor,dummy;
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+
+ // Get the cursor pos for the page
+ biosfn_get_cursor_pos(page,&dummy,&cursor);
+ xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8;
+
+ // Get the dimensions
+ nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
+ nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+
+ if(vga_modes[line].class==TEXT)
+  {
+   // Compute the address
+   address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2;
+
+   dummy=((Bit16u)attr<<8)+car;
+   memsetw(vga_modes[line].sstart,address,dummy,count);
+  }
+ else
+  {
+   // FIXME gfx mode not complete
+   cheight=video_param_table[line_to_vpti[line]].cheight;
+   bpp=vga_modes[line].pixbits;
+   while((count-->0) && (xcurs<nbcols))
+    {
+     switch(vga_modes[line].memmodel)
+      {
+       case PLANAR4:
+       case PLANAR1:
+         write_gfx_char_pl4(car,attr,xcurs,ycurs,nbcols,cheight);
+         break;
+       case CGA:
+         write_gfx_char_cga(car,attr,xcurs,ycurs,nbcols,bpp);
+         break;
+       case LINEAR8:
+         write_gfx_char_lin(car,attr,xcurs,ycurs,nbcols);
+         break;
+#ifdef DEBUG
+       default:
+         unimplemented();
+#endif
+      }
+     xcurs++;
+    }
+  }
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_write_char_only (car,page,attr,count)
+Bit8u car;Bit8u page;Bit8u attr;Bit16u count;
+{
+ Bit8u cheight,xcurs,ycurs,mode,line,bpp;
+ Bit16u nbcols,nbrows,address;
+ Bit16u cursor,dummy;
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+
+ // Get the cursor pos for the page
+ biosfn_get_cursor_pos(page,&dummy,&cursor);
+ xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8;
+
+ // Get the dimensions
+ nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
+ nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+
+ if(vga_modes[line].class==TEXT)
+  {
+   // Compute the address
+   address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2;
+
+   while(count-->0)
+    {write_byte(vga_modes[line].sstart,address,car);
+     address+=2;
+    }
+  }
+ else
+  {
+   // FIXME gfx mode not complete
+   cheight=video_param_table[line_to_vpti[line]].cheight;
+   bpp=vga_modes[line].pixbits;
+   while((count-->0) && (xcurs<nbcols))
+    {
+     switch(vga_modes[line].memmodel)
+      {
+       case PLANAR4:
+       case PLANAR1:
+         write_gfx_char_pl4(car,attr,xcurs,ycurs,nbcols,cheight);
+         break;
+       case CGA:
+         write_gfx_char_cga(car,attr,xcurs,ycurs,nbcols,bpp);
+         break;
+       case LINEAR8:
+         write_gfx_char_lin(car,attr,xcurs,ycurs,nbcols);
+         break;
+#ifdef DEBUG
+       default:
+         unimplemented();
+#endif
+      }
+     xcurs++;
+    }
+  }
+}
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_group_0B:
+  cmp   bh, #0x00
+  je    biosfn_set_border_color
+  cmp   bh, #0x01
+  je    biosfn_set_palette
+#ifdef DEBUG
+  call  _unknown
+#endif
+  ret
+biosfn_set_border_color:
+  push  ax
+  push  bx
+  push  cx
+  push  dx
+  mov   dx, # VGAREG_ACTL_RESET
+  in    al, dx
+  mov   dx, # VGAREG_ACTL_ADDRESS
+  mov   al, #0x00
+  out   dx, al
+  mov   al, bl
+  and   al, #0x0f
+  test  al, #0x08
+  jz    set_low_border
+  add   al, #0x08
+set_low_border:
+  out   dx, al
+  mov   cl, #0x01
+  and   bl, #0x10
+set_intensity_loop:
+  mov   dx, # VGAREG_ACTL_ADDRESS
+  mov   al, cl
+  out   dx, al
+  mov   dx, # VGAREG_ACTL_READ_DATA
+  in    al, dx
+  and   al, #0xef
+  or    al, bl
+  mov   dx, # VGAREG_ACTL_ADDRESS
+  out   dx, al
+  inc   cl
+  cmp   cl, #0x04
+  jne   set_intensity_loop
+  mov   al, #0x20
+  out   dx, al
+  pop   dx
+  pop   cx
+  pop   bx
+  pop   ax
+  ret
+biosfn_set_palette:
+  push  ax
+  push  bx
+  push  cx
+  push  dx
+  mov   dx, # VGAREG_ACTL_RESET
+  in    al, dx
+  mov   cl, #0x01
+  and   bl, #0x01
+set_cga_palette_loop:
+  mov   dx, # VGAREG_ACTL_ADDRESS
+  mov   al, cl
+  out   dx, al
+  mov   dx, # VGAREG_ACTL_READ_DATA
+  in    al, dx
+  and   al, #0xfe
+  or    al, bl
+  mov   dx, # VGAREG_ACTL_ADDRESS
+  out   dx, al
+  inc   cl
+  cmp   cl, #0x04
+  jne   set_cga_palette_loop
+  mov   al, #0x20
+  out   dx, al
+  pop   dx
+  pop   cx
+  pop   bx
+  pop   ax
+  ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_write_pixel (BH,AL,CX,DX) Bit8u BH;Bit8u AL;Bit16u CX;Bit16u DX;
+{
+ Bit8u mode,line,mask,attr,data;
+ Bit16u addr;
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+ if(vga_modes[line].class==TEXT)return;
+
+ switch(vga_modes[line].memmodel)
+  {
+   case PLANAR4:
+   case PLANAR1:
+     addr = CX/8+DX*read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+     mask = 0x80 >> (CX & 0x07);
+     outw(VGAREG_GRDC_ADDRESS, (mask << 8) | 0x08);
+     outw(VGAREG_GRDC_ADDRESS, 0x0205);
+     data = read_byte(0xa000,addr);
+     if (AL & 0x80)
+      {
+       outw(VGAREG_GRDC_ADDRESS, 0x1803);
+      }
+     write_byte(0xa000,addr,AL);
+ASM_START
+     mov dx, # VGAREG_GRDC_ADDRESS
+     mov ax, #0xff08
+     out dx, ax
+     mov ax, #0x0005
+     out dx, ax
+     mov ax, #0x0003
+     out dx, ax
+ASM_END
+     break;
+   case CGA:
+     if(vga_modes[line].pixbits==2)
+      {
+       addr=(CX>>2)+(DX>>1)*80;
+      }
+     else
+      {
+       addr=(CX>>3)+(DX>>1)*80;
+      }
+     if (DX & 1) addr += 0x2000;
+     data = read_byte(0xb800,addr);
+     if(vga_modes[line].pixbits==2)
+      {
+       attr = (AL & 0x03) << ((3 - (CX & 0x03)) * 2);
+       mask = 0x03 << ((3 - (CX & 0x03)) * 2);
+      }
+     else
+      {
+       attr = (AL & 0x01) << (7 - (CX & 0x07));
+       mask = 0x01 << (7 - (CX & 0x07));
+      }
+     if (AL & 0x80)
+      {
+       data ^= attr;
+      }
+     else
+      {
+       data &= ~mask;
+       data |= attr;
+      }
+     write_byte(0xb800,addr,data);
+     break;
+   case LINEAR8:
+     addr=CX+DX*(read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8);
+     write_byte(0xa000,addr,AL);
+     break;
+#ifdef DEBUG
+   default:
+     unimplemented();
+#endif
+  }
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_read_pixel (BH,CX,DX,AX) Bit8u BH;Bit16u CX;Bit16u DX;Bit16u *AX;
+{
+ Bit8u mode,line,mask,attr,data,i;
+ Bit16u addr;
+ Bit16u ss=get_SS();
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+ if(vga_modes[line].class==TEXT)return;
+
+ switch(vga_modes[line].memmodel)
+  {
+   case PLANAR4:
+   case PLANAR1:
+     addr = CX/8+DX*read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+     mask = 0x80 >> (CX & 0x07);
+     attr = 0x00;
+     for(i=0;i<4;i++)
+      {
+       outw(VGAREG_GRDC_ADDRESS, (i << 8) | 0x04);
+       data = read_byte(0xa000,addr) & mask;
+       if (data > 0) attr |= (0x01 << i);
+      }
+     break;
+   case CGA:
+     addr=(CX>>2)+(DX>>1)*80;
+     if (DX & 1) addr += 0x2000;
+     data = read_byte(0xb800,addr);
+     if(vga_modes[line].pixbits==2)
+      {
+       attr = (data >> ((3 - (CX & 0x03)) * 2)) & 0x03;
+      }
+     else
+      {
+       attr = (data >> (7 - (CX & 0x07))) & 0x01;
+      }
+     break;
+   case LINEAR8:
+     addr=CX+DX*(read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8);
+     attr=read_byte(0xa000,addr);
+     break;
+   default:
+#ifdef DEBUG
+     unimplemented();
+#endif
+     attr = 0;
+  }
+ write_word(ss,AX,(read_word(ss,AX) & 0xff00) | attr);
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_write_teletype (car, page, attr, flag) 
+Bit8u car;Bit8u page;Bit8u attr;Bit8u flag;
+{// flag = WITH_ATTR / NO_ATTR
+
+ Bit8u cheight,xcurs,ycurs,mode,line,bpp;
+ Bit16u nbcols,nbrows,address;
+ Bit16u cursor,dummy;
+
+ // special case if page is 0xff, use current page
+ if(page==0xff)
+  page=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE);
+
+ // Get the mode
+ mode=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE);
+ line=find_vga_entry(mode);
+ if(line==0xFF)return;
+
+ // Get the cursor pos for the page
+ biosfn_get_cursor_pos(page,&dummy,&cursor);
+ xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8;
+
+ // Get the dimensions
+ nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1;
+ nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+
+ switch(car)
+  {
+   case 7:
+    //FIXME should beep
+    break;
+
+   case 8:
+    if(xcurs>0)xcurs--;
+    break;
+
+   case '\r':
+    xcurs=0;
+    break;
+
+   case '\n':
+    ycurs++;
+    break;
+
+   case '\t':
+    do
+     {
+      biosfn_write_teletype(' ',page,attr,flag);
+      biosfn_get_cursor_pos(page,&dummy,&cursor);
+      xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8;
+     }while(xcurs%8==0);
+    break;
+
+   default:
+
+    if(vga_modes[line].class==TEXT)
+     {
+      // Compute the address  
+      address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2;
+
+      // Write the char 
+      write_byte(vga_modes[line].sstart,address,car);
+
+      if(flag==WITH_ATTR)
+       write_byte(vga_modes[line].sstart,address+1,attr);
+     }
+    else
+     {
+      // FIXME gfx mode not complete
+      cheight=video_param_table[line_to_vpti[line]].cheight;
+      bpp=vga_modes[line].pixbits;
+      switch(vga_modes[line].memmodel)
+       {
+        case PLANAR4:
+        case PLANAR1:
+          write_gfx_char_pl4(car,attr,xcurs,ycurs,nbcols,cheight);
+          break;
+        case CGA:
+          write_gfx_char_cga(car,attr,xcurs,ycurs,nbcols,bpp);
+          break;
+        case LINEAR8:
+          write_gfx_char_lin(car,attr,xcurs,ycurs,nbcols);
+          break;
+#ifdef DEBUG
+        default:
+          unimplemented();
+#endif
+       }
+     }
+    xcurs++;
+  }
+
+ // Do we need to wrap ?
+ if(xcurs==nbcols)
+  {xcurs=0;
+   ycurs++;
+  }
+
+ // Do we need to scroll ?
+ if(ycurs==nbrows)
+  {
+   if(vga_modes[line].class==TEXT)
+    {
+     address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+(ycurs-1)*nbcols)*2;
+     attr=read_byte(vga_modes[line].sstart,address+1);
+     biosfn_scroll(0x01,attr,0,0,nbrows-1,nbcols-1,page,SCROLL_UP);
+    }
+   else
+    {
+     biosfn_scroll(0x01,0x00,0,0,nbrows-1,nbcols-1,page,SCROLL_UP);
+    }
+   ycurs-=1;
+  }
+
+ // Set the cursor for the page
+ cursor=ycurs; cursor<<=8; cursor+=xcurs;
+ biosfn_set_cursor_pos(page,cursor);
+}
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_get_video_mode:
+  push  ds
+  mov   ax, # BIOSMEM_SEG
+  mov   ds, ax
+  push  bx
+  mov   bx, # BIOSMEM_CURRENT_PAGE
+  mov   al, [bx]
+  pop   bx
+  mov   bh, al
+  push  bx
+  mov   bx, # BIOSMEM_VIDEO_CTL
+  mov   ah, [bx]
+  and   ah, #0x80
+  mov   bx, # BIOSMEM_CURRENT_MODE
+  mov   al, [bx]
+  or    al, ah
+  mov   bx, # BIOSMEM_NB_COLS
+  mov   ah, [bx]
+  pop   bx
+  pop   ds
+  ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_group_10:
+  cmp   al, #0x00
+  jne   int10_test_1001
+  jmp   biosfn_set_single_palette_reg
+int10_test_1001:
+  cmp   al, #0x01
+  jne   int10_test_1002
+  jmp   biosfn_set_overscan_border_color
+int10_test_1002:
+  cmp   al, #0x02
+  jne   int10_test_1003
+  jmp   biosfn_set_all_palette_reg
+int10_test_1003:
+  cmp   al, #0x03
+  jne   int10_test_1007
+  jmp   biosfn_toggle_intensity
+int10_test_1007:
+  cmp   al, #0x07
+  jne   int10_test_1008
+  jmp   biosfn_get_single_palette_reg
+int10_test_1008:
+  cmp   al, #0x08
+  jne   int10_test_1009
+  jmp   biosfn_read_overscan_border_color
+int10_test_1009:
+  cmp   al, #0x09
+  jne   int10_test_1010
+  jmp   biosfn_get_all_palette_reg
+int10_test_1010:
+  cmp   al, #0x10
+  jne   int10_test_1012
+  jmp  biosfn_set_single_dac_reg
+int10_test_1012:
+  cmp   al, #0x12
+  jne   int10_test_1013
+  jmp   biosfn_set_all_dac_reg
+int10_test_1013:
+  cmp   al, #0x13
+  jne   int10_test_1015
+  jmp   biosfn_select_video_dac_color_page
+int10_test_1015:
+  cmp   al, #0x15
+  jne   int10_test_1017
+  jmp   biosfn_read_single_dac_reg
+int10_test_1017:
+  cmp   al, #0x17
+  jne   int10_test_1018
+  jmp   biosfn_read_all_dac_reg
+int10_test_1018:
+  cmp   al, #0x18
+  jne   int10_test_1019
+  jmp   biosfn_set_pel_mask
+int10_test_1019:
+  cmp   al, #0x19
+  jne   int10_test_101A
+  jmp   biosfn_read_pel_mask
+int10_test_101A:
+  cmp   al, #0x1a
+  jne   int10_group_10_unknown
+  jmp   biosfn_read_video_dac_state
+int10_group_10_unknown:
+#ifdef DEBUG
+  call  _unknown
+#endif
+  ret
+
+biosfn_set_single_palette_reg:
+  cmp   bl, #0x14
+  ja    no_actl_reg1
+  push  ax
+  push  dx
+  mov   dx, # VGAREG_ACTL_RESET
+  in    al, dx
+  mov   dx, # VGAREG_ACTL_ADDRESS
+  mov   al, bl
+  out   dx, al
+  mov   al, bh
+  out   dx, al
+  mov   al, #0x20
+  out   dx, al
+  pop   dx
+  pop   ax
+no_actl_reg1:
+  ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_set_overscan_border_color:
+  push  bx
+  mov   bl, #0x11
+  call  biosfn_set_single_palette_reg
+  pop   bx
+  ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_set_all_palette_reg:
+  push  ax
+  push  bx
+  push  cx
+  push  dx
+  mov   bx, dx
+  mov   dx, # VGAREG_ACTL_RESET
+  in    al, dx
+  mov   cl, #0x00
+  mov   dx, # VGAREG_ACTL_ADDRESS
+set_palette_loop:
+  mov   al, cl
+  out   dx, al
+  seg   es
+  mov   al, [bx]
+  out   dx, al
+  inc   bx
+  inc   cl
+  cmp   cl, #0x10
+  jne   set_palette_loop
+  mov   al, #0x11
+  out   dx, al
+  seg   es
+  mov   al, [bx]
+  out   dx, al
+  mov   al, #0x20
+  out   dx, al
+  pop   dx
+  pop   cx
+  pop   bx
+  pop   ax
+  ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_toggle_intensity:
+  push  ax
+  push  bx
+  push  dx
+  mov   dx, # VGAREG_ACTL_RESET
+  in    al, dx
+  mov   dx, # VGAREG_ACTL_ADDRESS
+  mov   al, #0x10
+  out   dx, al
+  mov   dx, # VGAREG_ACTL_READ_DATA
+  in    al, dx
+  and   al, #0xf7
+  and   bl, #0x01
+  shl   bl, 3
+  or    al, bl
+  mov   dx, # VGAREG_ACTL_ADDRESS
+  out   dx, al
+  mov   al, #0x20
+  out   dx, al
+  pop   dx
+  pop   bx
+  pop   ax
+  ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_get_single_palette_reg:
+  cmp   bl, #0x14
+  ja    no_actl_reg2
+  push  ax
+  push  dx
+  mov   dx, # VGAREG_ACTL_RESET
+  in    al, dx
+  mov   dx, # VGAREG_ACTL_ADDRESS
+  mov   al, bl
+  out   dx, al
+  mov   dx, # VGAREG_ACTL_READ_DATA
+  in    al, dx
+  mov   bh, al
+  mov   dx, # VGAREG_ACTL_RESET
+  in    al, dx
+  mov   dx, # VGAREG_ACTL_ADDRESS
+  mov   al, #0x20
+  out   dx, al
+  pop   dx
+  pop   ax
+no_actl_reg2:
+  ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_read_overscan_border_color:
+  push  ax
+  push  bx
+  mov   bl, #0x11
+  call  biosfn_get_single_palette_reg
+  mov   al, bh
+  pop   bx
+  mov   bh, al
+  pop   ax
+  ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_get_all_palette_reg:
+  push  ax
+  push  bx
+  push  cx
+  push  dx
+  mov   bx, dx
+  mov   cl, #0x00
+get_palette_loop:
+  mov   dx, # VGAREG_ACTL_RESET
+  in    al, dx
+  mov   dx, # VGAREG_ACTL_ADDRESS
+  mov   al, cl
+  out   dx, al
+  mov   dx, # VGAREG_ACTL_READ_DATA
+  in    al, dx
+  seg   es
+  mov   [bx], al
+  inc   bx
+  inc   cl
+  cmp   cl, #0x10
+  jne   get_palette_loop
+  mov   dx, # VGAREG_ACTL_RESET
+  in    al, dx
+  mov   dx, # VGAREG_ACTL_ADDRESS
+  mov   al, #0x11
+  out   dx, al
+  mov   dx, # VGAREG_ACTL_READ_DATA
+  in    al, dx
+  seg   es
+  mov   [bx], al
+  mov   dx, # VGAREG_ACTL_RESET
+  in    al, dx
+  mov   dx, # VGAREG_ACTL_ADDRESS
+  mov   al, #0x20
+  out   dx, al
+  pop   dx
+  pop   cx
+  pop   bx
+  pop   ax
+  ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_set_single_dac_reg:
+  push  ax
+  push  dx
+  mov   dx, # VGAREG_DAC_WRITE_ADDRESS
+  mov   al, bl
+  out   dx, al
+  mov   dx, # VGAREG_DAC_DATA
+  pop   ax
+  push  ax
+  mov   al, ah
+  out   dx, al
+  mov   al, ch
+  out   dx, al
+  mov   al, cl
+  out   dx, al
+  pop   dx
+  pop   ax
+  ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_set_all_dac_reg:
+  push  ax
+  push  bx
+  push  cx
+  push  dx
+  mov   dx, # VGAREG_DAC_WRITE_ADDRESS
+  mov   al, bl
+  out   dx, al
+  pop   dx
+  push  dx
+  mov   bx, dx
+  mov   dx, # VGAREG_DAC_DATA
+set_dac_loop:
+  seg   es
+  mov   al, [bx]
+  out   dx, al
+  inc   bx
+  seg   es
+  mov   al, [bx]
+  out   dx, al
+  inc   bx
+  seg   es
+  mov   al, [bx]
+  out   dx, al
+  inc   bx
+  dec   cx
+  jnz   set_dac_loop
+  pop   dx
+  pop   cx
+  pop   bx
+  pop   ax
+  ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_select_video_dac_color_page:
+  push  ax
+  push  bx
+  push  dx
+  mov   dx, # VGAREG_ACTL_RESET
+  in    al, dx
+  mov   dx, # VGAREG_ACTL_ADDRESS
+  mov   al, #0x10
+  out   dx, al
+  mov   dx, # VGAREG_ACTL_READ_DATA
+  in    al, dx
+  and   bl, #0x01
+  jnz   set_dac_page
+  and   al, #0x7f
+  shl   bh, 7
+  or    al, bh
+  mov   dx, # VGAREG_ACTL_ADDRESS
+  out   dx, al
+  jmp   set_actl_normal
+set_dac_page:
+  push  ax
+  mov   dx, # VGAREG_ACTL_RESET
+  in    al, dx
+  mov   dx, # VGAREG_ACTL_ADDRESS
+  mov   al, #0x14
+  out   dx, al
+  pop   ax
+  and   al, #0x80
+  jnz   set_dac_16_page
+  shl   bh, 2
+set_dac_16_page:
+  and   bh, #0x0f
+  mov   al, bh
+  out   dx, al
+set_actl_normal:
+  mov   al, #0x20
+  out   dx, al
+  pop   dx
+  pop   bx
+  pop   ax
+  ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_read_single_dac_reg:
+  push  ax
+  push  dx
+  mov   dx, # VGAREG_DAC_READ_ADDRESS
+  mov   al, bl
+  out   dx, al
+  pop   ax
+  mov   ah, al
+  mov   dx, # VGAREG_DAC_DATA
+  in    al, dx
+  xchg  al, ah
+  push  ax
+  in    al, dx
+  mov   ch, al
+  in    al, dx
+  mov   cl, al
+  pop   dx
+  pop   ax
+  ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_read_all_dac_reg:
+  push  ax
+  push  bx
+  push  cx
+  push  dx
+  mov   dx, # VGAREG_DAC_READ_ADDRESS
+  mov   al, bl
+  out   dx, al
+  pop   dx
+  push  dx
+  mov   bx, dx
+  mov   dx, # VGAREG_DAC_DATA
+read_dac_loop:
+  in    al, dx
+  seg   es
+  mov   [bx], al
+  inc   bx
+  in    al, dx
+  seg   es
+  mov   [bx], al
+  inc   bx
+  in    al, dx
+  seg   es
+  mov   [bx], al
+  inc   bx
+  dec   cx
+  jnz   read_dac_loop
+  pop   dx
+  pop   cx
+  pop   bx
+  pop   ax
+  ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_set_pel_mask:
+  push  ax
+  push  dx
+  mov   dx, # VGAREG_PEL_MASK
+  mov   al, bl
+  out   dx, al
+  pop   dx
+  pop   ax
+  ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_read_pel_mask:
+  push  ax
+  push  dx
+  mov   dx, # VGAREG_PEL_MASK
+  in    al, dx
+  mov   bl, al
+  pop   dx
+  pop   ax
+  ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_read_video_dac_state:
+  push  ax
+  push  dx
+  mov   dx, # VGAREG_ACTL_RESET
+  in    al, dx
+  mov   dx, # VGAREG_ACTL_ADDRESS
+  mov   al, #0x10
+  out   dx, al
+  mov   dx, # VGAREG_ACTL_READ_DATA
+  in    al, dx
+  mov   bl, al
+  shr   bl, 7
+  mov   dx, # VGAREG_ACTL_RESET
+  in    al, dx
+  mov   dx, # VGAREG_ACTL_ADDRESS
+  mov   al, #0x14
+  out   dx, al
+  mov   dx, # VGAREG_ACTL_READ_DATA
+  in    al, dx
+  mov   bh, al
+  and   bh, #0x0f
+  test  bl, #0x01
+  jnz   get_dac_16_page
+  shr   bh, 2
+get_dac_16_page:
+  mov   dx, # VGAREG_ACTL_RESET
+  in    al, dx
+  mov   dx, # VGAREG_ACTL_ADDRESS
+  mov   al, #0x20
+  out   dx, al
+  pop   dx
+  pop   ax
+  ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_perform_gray_scale_summing (start,count) 
+Bit16u start;Bit16u count;
+{Bit8u r,g,b;
+ Bit16u i;
+ Bit16u index;
+
+ inb(VGAREG_ACTL_RESET);
+ outb(VGAREG_ACTL_ADDRESS,0x00);
+
+ for( index = 0; index < count; index++ ) 
+  {
+   // set read address and switch to read mode
+   outb(VGAREG_DAC_READ_ADDRESS,start);
+   // get 6-bit wide RGB data values
+   r=inb( VGAREG_DAC_DATA );
+   g=inb( VGAREG_DAC_DATA );
+   b=inb( VGAREG_DAC_DATA );
+
+   // intensity = ( 0.3 * Red ) + ( 0.59 * Green ) + ( 0.11 * Blue )
+   i = ( ( 77*r + 151*g + 28*b ) + 0x80 ) >> 8;
+
+   if(i>0x3f)i=0x3f;
+   // set write address and switch to write mode
+   outb(VGAREG_DAC_WRITE_ADDRESS,start);
+   // write new intensity value
+   outb( VGAREG_DAC_DATA, i&0xff );
+   outb( VGAREG_DAC_DATA, i&0xff );
+   outb( VGAREG_DAC_DATA, i&0xff );
+   start++;
+  }  
+ inb(VGAREG_ACTL_RESET);
+ outb(VGAREG_ACTL_ADDRESS,0x20);
+}
+
+// --------------------------------------------------------------------------------------------
+static void get_font_access()
+{
+ASM_START
+ mov dx, # VGAREG_SEQU_ADDRESS
+ mov ax, #0x0100
+ out dx, ax
+ mov ax, #0x0402
+ out dx, ax
+ mov ax, #0x0704
+ out dx, ax
+ mov ax, #0x0300
+ out dx, ax
+ mov dx, # VGAREG_GRDC_ADDRESS
+ mov ax, #0x0204
+ out dx, ax
+ mov ax, #0x0005
+ out dx, ax
+ mov ax, #0x0406
+ out dx, ax
+ASM_END
+}
+
+static void release_font_access()
+{
+ASM_START
+ mov dx, # VGAREG_SEQU_ADDRESS
+ mov ax, #0x0100
+ out dx, ax
+ mov ax, #0x0302
+ out dx, ax
+ mov ax, #0x0304
+ out dx, ax
+ mov ax, #0x0300
+ out dx, ax
+ mov dx, # VGAREG_READ_MISC_OUTPUT
+ in  al, dx
+ and al, #0x01
+ shl al, 2
+ or  al, #0x0a
+ mov ah, al
+ mov al, #0x06
+ mov dx, # VGAREG_GRDC_ADDRESS
+ out dx, ax
+ mov ax, #0x0004
+ out dx, ax
+ mov ax, #0x1005
+ out dx, ax
+ASM_END
+}
+
+ASM_START
+idiv_u:
+  xor dx,dx
+  div bx
+  ret
+ASM_END
+
+static void set_scan_lines(lines) Bit8u lines;
+{
+ Bit16u crtc_addr,cols,page,vde;
+ Bit8u crtc_r9,ovl,rows;
+
+ crtc_addr = read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS);
+ outb(crtc_addr, 0x09);
+ crtc_r9 = inb(crtc_addr+1);
+ crtc_r9 = (crtc_r9 & 0xe0) | (lines - 1);
+ outb(crtc_addr+1, crtc_r9);
+ if(lines==8)
+  {
+   biosfn_set_cursor_shape(0x06,0x07);
+  }
+ else
+  {
+   biosfn_set_cursor_shape(lines-4,lines-3);
+  }
+ write_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT, lines);
+ outb(crtc_addr, 0x12);
+ vde = inb(crtc_addr+1);
+ outb(crtc_addr, 0x07);
+ ovl = inb(crtc_addr+1);
+ vde += (((ovl & 0x02) << 7) + ((ovl & 0x40) << 3) + 1);
+ rows = vde / lines;
+ write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, rows-1);
+ cols = read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS);
+ write_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE, rows * cols * 2);
+}
+
+static void biosfn_load_text_user_pat (AL,ES,BP,CX,DX,BL,BH) Bit8u AL;Bit16u ES;Bit16u BP;Bit16u CX;Bit16u DX;Bit8u BL;Bit8u BH;
+{
+ Bit16u blockaddr,dest,i,src;
+
+ get_font_access();
+ blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
+ for(i=0;i<CX;i++)
+  {
+   src = BP + i * BH;
+   dest = blockaddr + (DX + i) * 32;
+   memcpyb(0xA000, dest, ES, src, BH);
+  }
+ release_font_access();
+ if(AL>=0x10)
+  {
+   set_scan_lines(BH);
+  }
+}
+
+static void biosfn_load_text_8_14_pat (AL,BL) Bit8u AL;Bit8u BL;
+{
+ Bit16u blockaddr,dest,i,src;
+
+ get_font_access();
+ blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
+ for(i=0;i<0x100;i++)
+  {
+   src = i * 14;
+   dest = blockaddr + i * 32;
+   memcpyb(0xA000, dest, 0xC000, vgafont14+src, 14);
+  }
+ release_font_access();
+ if(AL>=0x10)
+  {
+   set_scan_lines(14);
+  }
+}
+
+static void biosfn_load_text_8_8_pat (AL,BL) Bit8u AL;Bit8u BL;
+{
+ Bit16u blockaddr,dest,i,src;
+
+ get_font_access();
+ blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
+ for(i=0;i<0x100;i++)
+  {
+   src = i * 8;
+   dest = blockaddr + i * 32;
+   memcpyb(0xA000, dest, 0xC000, vgafont8+src, 8);
+  }
+ release_font_access();
+ if(AL>=0x10)
+  {
+   set_scan_lines(8);
+  }
+}
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_set_text_block_specifier:
+  push  ax
+  push  dx
+  mov   dx, # VGAREG_SEQU_ADDRESS
+  mov   ah, bl
+  mov   al, #0x03
+  out   dx, ax
+  pop   dx
+  pop   ax
+  ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_load_text_8_16_pat (AL,BL) Bit8u AL;Bit8u BL;
+{
+ Bit16u blockaddr,dest,i,src;
+
+ get_font_access();
+ blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
+ for(i=0;i<0x100;i++)
+  {
+   src = i * 16;
+   dest = blockaddr + i * 32;
+   memcpyb(0xA000, dest, 0xC000, vgafont16+src, 16);
+  }
+ release_font_access();
+ if(AL>=0x10)
+  {
+   set_scan_lines(16);
+  }
+}
+
+static void biosfn_load_gfx_8_8_chars (ES,BP) Bit16u ES;Bit16u BP;
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+static void biosfn_load_gfx_user_chars (ES,BP,CX,BL,DL) Bit16u ES;Bit16u BP;Bit16u CX;Bit8u BL;Bit8u DL;
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+static void biosfn_load_gfx_8_14_chars (BL) Bit8u BL;
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+static void biosfn_load_gfx_8_8_dd_chars (BL) Bit8u BL;
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+static void biosfn_load_gfx_8_16_chars (BL) Bit8u BL;
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+// --------------------------------------------------------------------------------------------
+static void biosfn_get_font_info (BH,ES,BP,CX,DX) 
+Bit8u BH;Bit16u *ES;Bit16u *BP;Bit16u *CX;Bit16u *DX;
+{Bit16u ss=get_SS();
+ switch(BH)
+  {case 0x00:
+    write_word(ss,ES,read_word(0x00,0x1f*4));
+    write_word(ss,BP,read_word(0x00,(0x1f*4)+2));
+    break;
+   case 0x01:
+    write_word(ss,ES,read_word(0x00,0x43*4));
+    write_word(ss,BP,read_word(0x00,(0x43*4)+2));
+    break;
+   case 0x02:
+    write_word(ss,ES,0xC000);
+    write_word(ss,BP,vgafont14);
+    break;
+   case 0x03:
+    write_word(ss,ES,0xC000);
+    write_word(ss,BP,vgafont8);
+    break;
+   case 0x04:
+    write_word(ss,ES,0xC000);
+    write_word(ss,BP,vgafont8+128*8);
+    break;
+   case 0x05:
+    write_word(ss,ES,0xC000);
+    write_word(ss,BP,vgafont14alt);
+    break;
+   case 0x06:
+    write_word(ss,ES,0xC000);
+    write_word(ss,BP,vgafont16);
+    break;
+   case 0x07:
+    write_word(ss,ES,0xC000);
+    write_word(ss,BP,vgafont16alt);
+    break;
+   default:
+    #ifdef DEBUG
+     printf("Get font info BH(%02x) was discarded\n",BH);
+    #endif
+    return;
+  }
+ // Set byte/char of on screen font
+ write_word(ss,CX,(Bit16u)read_byte(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT));
+
+ // Set Highest char row
+ write_word(ss,DX,(Bit16u)read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS));
+}
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_get_ega_info:
+  push  ds
+  push  ax
+  mov   ax, # BIOSMEM_SEG
+  mov   ds, ax
+  xor   ch, ch
+  mov   bx, # BIOSMEM_SWITCHES
+  mov   cl, [bx]
+  and   cl, #0x0f
+  mov   bx, # BIOSMEM_CRTC_ADDRESS
+  mov   ax, [bx]
+  mov   bx, #0x0003
+  cmp   ax, # VGAREG_MDA_CRTC_ADDRESS
+  jne   mode_ega_color
+  mov   bh, #0x01
+mode_ega_color:
+  pop   ax
+  pop   ds
+  ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_alternate_prtsc()
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_select_vert_res:
+
+; res : 00 200 lines, 01 350 lines, 02 400 lines
+
+  push  ds
+  push  bx
+  push  dx
+  mov   dl, al
+  mov   ax, # BIOSMEM_SEG
+  mov   ds, ax
+  mov   bx, # BIOSMEM_MODESET_CTL
+  mov   al, [bx]
+  mov   bx, # BIOSMEM_SWITCHES
+  mov   ah, [bx]
+  cmp   dl, #0x01
+  je    vert_res_350
+  jb    vert_res_200
+  cmp   dl, #0x02
+  je    vert_res_400
+#ifdef DEBUG
+  mov   al, dl
+  xor   ah, ah
+  push  ax
+  mov   bx, #msg_vert_res
+  push  bx
+  call  _printf
+  add   sp, #4
+#endif
+  jmp   set_retcode
+vert_res_400:
+
+  ; reset modeset ctl bit 7 and set bit 4
+  ; set switches bit 3-0 to 0x09
+
+  and   al, #0x7f
+  or    al, #0x10
+  and   ah, #0xf0
+  or    ah, #0x09
+  jnz   set_vert_res
+vert_res_350:
+
+  ; reset modeset ctl bit 7 and bit 4
+  ; set switches bit 3-0 to 0x09
+
+  and   al, #0x6f
+  and   ah, #0xf0
+  or    ah, #0x09
+  jnz   set_vert_res
+vert_res_200:
+
+  ; set modeset ctl bit 7 and reset bit 4
+  ; set switches bit 3-0 to 0x08
+
+  and   al, #0xef
+  or    al, #0x80
+  and   ah, #0xf0
+  or    ah, #0x08
+set_vert_res:
+  mov   bx, # BIOSMEM_MODESET_CTL
+  mov   [bx], al
+  mov   bx, # BIOSMEM_SWITCHES
+  mov   [bx], ah
+set_retcode:
+  mov   ax, #0x1212
+  pop   dx
+  pop   bx
+  pop   ds
+  ret
+
+#ifdef DEBUG
+msg_vert_res:
+.ascii "Select vert res (%02x) was discarded"
+.byte 0x0d,0x0a,0x00
+#endif
+
+
+biosfn_enable_default_palette_loading:
+  push  ds
+  push  bx
+  push  dx
+  mov   dl, al
+  and   dl, #0x01
+  shl   dl, 3
+  mov   ax, # BIOSMEM_SEG
+  mov   ds, ax
+  mov   bx, # BIOSMEM_MODESET_CTL
+  mov   al, [bx]
+  and   al, #0xf7
+  or    al, dl
+  mov   [bx], al
+  mov   ax, #0x1212
+  pop   dx
+  pop   bx
+  pop   ds
+  ret
+
+
+biosfn_enable_video_addressing:
+  push  bx
+  push  dx
+  mov   bl, al
+  and   bl, #0x01
+  xor   bl, #0x01
+  shl   bl, 1
+  mov   dx, # VGAREG_READ_MISC_OUTPUT
+  in    al, dx
+  and   al, #0xfd
+  or    al, bl
+  mov   dx, # VGAREG_WRITE_MISC_OUTPUT
+  out   dx, al
+  mov   ax, #0x1212
+  pop   dx
+  pop   bx
+  ret
+
+
+biosfn_enable_grayscale_summing:
+  push  ds
+  push  bx
+  push  dx
+  mov   dl, al
+  and   dl, #0x01
+  xor   dl, #0x01
+  shl   dl, 1
+  mov   ax, # BIOSMEM_SEG
+  mov   ds, ax
+  mov   bx, # BIOSMEM_MODESET_CTL
+  mov   al, [bx]
+  and   al, #0xfd
+  or    al, dl
+  mov   [bx], al
+  mov   ax, #0x1212
+  pop   dx
+  pop   bx
+  pop   ds
+  ret
+
+
+biosfn_enable_cursor_emulation:
+  push  ds
+  push  bx
+  push  dx
+  mov   dl, al
+  and   dl, #0x01
+  xor   dl, #0x01
+  mov   ax, # BIOSMEM_SEG
+  mov   ds, ax
+  mov   bx, # BIOSMEM_MODESET_CTL
+  mov   al, [bx]
+  and   al, #0xfe
+  or    al, dl
+  mov   [bx], al
+  mov   ax, #0x1212
+  pop   dx
+  pop   bx
+  pop   ds
+  ret
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_switch_video_interface (AL,ES,DX) Bit8u AL;Bit16u ES;Bit16u DX;
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+static void biosfn_enable_video_refresh_control (AL) Bit8u AL;
+{
+#ifdef DEBUG
+ unimplemented();
+#endif
+}
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_write_string (flag,page,attr,count,row,col,seg,offset) 
+Bit8u flag;Bit8u page;Bit8u attr;Bit16u count;Bit8u row;Bit8u col;Bit16u seg;Bit16u offset;
+{
+ Bit16u newcurs,oldcurs,dummy;
+ Bit8u car,carattr;
+
+ // Read curs info for the page
+ biosfn_get_cursor_pos(page,&dummy,&oldcurs);
+
+ // if row=0xff special case : use current cursor position
+ if(row==0xff)
+  {col=oldcurs&0x00ff;
+   row=(oldcurs&0xff00)>>8;
+  }
+
+ newcurs=row; newcurs<<=8; newcurs+=col;
+ biosfn_set_cursor_pos(page,newcurs);
+ while(count--!=0)
+  {
+   car=read_byte(seg,offset++);
+   if((flag&0x02)!=0)
+    attr=read_byte(seg,offset++);
+
+   biosfn_write_teletype(car,page,attr,WITH_ATTR);
+  }
+ // Set back curs pos 
+ if((flag&0x01)==0)
+  biosfn_set_cursor_pos(page,oldcurs);
+}
+
+// --------------------------------------------------------------------------------------------
+ASM_START
+biosfn_group_1A:
+  cmp   al, #0x00
+  je    biosfn_read_display_code
+  cmp   al, #0x01
+  je    biosfn_set_display_code
+#ifdef DEBUG
+  call  _unknown
+#endif
+  ret
+biosfn_read_display_code:
+  push  ds
+  push  ax
+  mov   ax, # BIOSMEM_SEG
+  mov   ds, ax
+  mov   bx, # BIOSMEM_DCC_INDEX
+  mov   al, [bx]
+  mov   bl, al
+  xor   bh, bh
+  pop   ax
+  mov   al, ah
+  pop   ds
+  ret
+biosfn_set_display_code:
+  push  ds
+  push  ax
+  push  bx
+  mov   ax, # BIOSMEM_SEG
+  mov   ds, ax
+  mov   ax, bx
+  mov   bx, # BIOSMEM_DCC_INDEX
+  mov   [bx], al
+#ifdef DEBUG
+  mov   al, ah
+  xor   ah, ah
+  push  ax
+  mov   bx, #msg_alt_dcc
+  push  bx
+  call  _printf
+  add   sp, #4
+#endif
+  pop   bx
+  pop   ax
+  mov   al, ah
+  pop   ds
+  ret
+
+#ifdef DEBUG
+msg_alt_dcc:
+.ascii "Alternate Display code (%02x) was discarded"
+.byte 0x0d,0x0a,0x00
+#endif
+ASM_END
+
+// --------------------------------------------------------------------------------------------
+static void biosfn_read_state_info (BX,ES,DI) 
+Bit16u BX;Bit16u ES;Bit16u DI;
+{
+ // Address of static functionality table
+ write_word(ES,DI+0x00,&static_functionality);
+ write_word(ES,DI+0x02,0xC000);
+
+ // Hard coded copy from BIOS area. Should it be cleaner ?
+ memcpyb(ES,DI+0x04,BIOSMEM_SEG,0x49,30);
+ memcpyb(ES,DI+0x22,BIOSMEM_SEG,0x84,3);
+ write_byte(ES,DI+0x25,read_byte(BIOSMEM_SEG,BIOSMEM_DCC_INDEX));
+ write_byte(ES,DI+0x26,0);
+ write_byte(ES,DI+0x27,16);
+ write_byte(ES,DI+0x28,0);
+ write_byte(ES,DI+0x29,8);
+ write_byte(ES,DI+0x2a,2);
+ write_byte(ES,DI+0x2b,0);
+ write_byte(ES,DI+0x2c,0);
+ write_byte(ES,DI+0x31,3);
+ write_byte(ES,DI+0x32,0);
+ memsetb(ES,DI+0x33,0,13);
+}
+
+// --------------------------------------------------------------------------------------------
+// --------------------------------------------------------------------------------------------
+static Bit16u biosfn_read_video_state_size2 (CX) 
+     Bit16u CX;
+{
+    Bit16u size;
+    size = 0;
+    if (CX & 1) {
+        size += 0x46;
+    }
+    if (CX & 2) {
+        size += (5 + 8 + 5) * 2 + 6;
+    }
+    if (CX & 4) {
+        size += 3 + 256 * 3 + 1;
+}
+    return size;
+}
+
+static void biosfn_read_video_state_size (CX, BX) 
+     Bit16u CX; Bit16u *BX;
+{
+    Bit16u ss=get_SS();
+    write_word(ss, BX, biosfn_read_video_state_size2(CX));
+}
+
+static Bit16u biosfn_save_video_state (CX,ES,BX) 
+     Bit16u CX;Bit16u ES;Bit16u BX;
+{
+    Bit16u i, v, crtc_addr, ar_index;
+
+    crtc_addr = read_word(BIOSMEM_SEG, BIOSMEM_CRTC_ADDRESS);
+    if (CX & 1) {
+        write_byte(ES, BX, inb(VGAREG_SEQU_ADDRESS)); BX++;
+        write_byte(ES, BX, inb(crtc_addr)); BX++;
+        write_byte(ES, BX, inb(VGAREG_GRDC_ADDRESS)); BX++;
+        inb(VGAREG_ACTL_RESET);
+        ar_index = inb(VGAREG_ACTL_ADDRESS);
+        write_byte(ES, BX, ar_index); BX++;
+        write_byte(ES, BX, inb(VGAREG_READ_FEATURE_CTL)); BX++;
+
+        for(i=1;i<=4;i++){
+            outb(VGAREG_SEQU_ADDRESS, i);
+            write_byte(ES, BX, inb(VGAREG_SEQU_DATA)); BX++;
+        }
+        outb(VGAREG_SEQU_ADDRESS, 0);
+        write_byte(ES, BX, inb(VGAREG_SEQU_DATA)); BX++;
+
+        for(i=0;i<=0x18;i++) {
+            outb(crtc_addr,i);
+            write_byte(ES, BX, inb(crtc_addr+1)); BX++;
+        }
+
+        for(i=0;i<=0x13;i++) {
+            inb(VGAREG_ACTL_RESET);
+            outb(VGAREG_ACTL_ADDRESS, i | (ar_index & 0x20));
+            write_byte(ES, BX, inb(VGAREG_ACTL_READ_DATA)); BX++;
+        }
+        inb(VGAREG_ACTL_RESET);
+
+        for(i=0;i<=8;i++) {
+            outb(VGAREG_GRDC_ADDRESS,i);
+            write_byte(ES, BX, inb(VGAREG_GRDC_DATA)); BX++;
+        }
+
+        write_word(ES, BX, crtc_addr); BX+= 2;
+
+        /* XXX: read plane latches */
+        write_byte(ES, BX, 0); BX++;
+        write_byte(ES, BX, 0); BX++;
+        write_byte(ES, BX, 0); BX++;
+        write_byte(ES, BX, 0); BX++;
+    }
+    if (CX & 2) {
+        write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE)); BX++;
+        write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS)); BX += 2;
+        write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE)); BX += 2;
+        write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS)); BX += 2;
+        write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)); BX++;
+        write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT)); BX += 2;
+        write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL)); BX++;
+        write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES)); BX++;
+        write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL)); BX++;
+        write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE)); BX += 2;
+        for(i=0;i<8;i++) {
+            write_word(ES, BX, read_word(BIOSMEM_SEG, BIOSMEM_CURSOR_POS+2*i));
+            BX += 2;
+        }
+        write_word(ES, BX, read_word(BIOSMEM_SEG,BIOSMEM_CURRENT_START)); BX += 2;
+        write_byte(ES, BX, read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE)); BX++;
+        /* current font */
+        write_word(ES, BX, read_word(0, 0x1f * 4)); BX += 2;
+        write_word(ES, BX, read_word(0, 0x1f * 4 + 2)); BX += 2;
+        write_word(ES, BX, read_word(0, 0x43 * 4)); BX += 2;
+        write_word(ES, BX, read_word(0, 0x43 * 4 + 2)); BX += 2;
+    }
+    if (CX & 4) {
+        /* XXX: check this */
+        write_byte(ES, BX, inb(VGAREG_DAC_STATE)); BX++; /* read/write mode dac */
+        write_byte(ES, BX, inb(VGAREG_DAC_WRITE_ADDRESS)); BX++; /* pix address */
+        write_byte(ES, BX, inb(VGAREG_PEL_MASK)); BX++;
+        // Set the whole dac always, from 0
+        outb(VGAREG_DAC_WRITE_ADDRESS,0x00);
+        for(i=0;i<256*3;i++) {
+            write_byte(ES, BX, inb(VGAREG_DAC_DATA)); BX++;
+        }
+        write_byte(ES, BX, 0); BX++; /* color select register */
+    }
+    return BX;
+}
+
+static Bit16u biosfn_restore_video_state (CX,ES,BX) 
+     Bit16u CX;Bit16u ES;Bit16u BX;
+{
+    Bit16u i, crtc_addr, v, addr1, ar_index;
+
+    if (CX & 1) {
+        // Reset Attribute Ctl flip-flop
+        inb(VGAREG_ACTL_RESET);
+
+        crtc_addr = read_word(ES, BX + 0x40);
+        addr1 = BX;
+        BX += 5;
+        
+        for(i=1;i<=4;i++){
+            outb(VGAREG_SEQU_ADDRESS, i);
+            outb(VGAREG_SEQU_DATA, read_byte(ES, BX)); BX++;
+        }
+        outb(VGAREG_SEQU_ADDRESS, 0);
+        outb(VGAREG_SEQU_DATA, read_byte(ES, BX)); BX++;
+
+        // Disable CRTC write protection
+        outw(crtc_addr,0x0011);
+        // Set CRTC regs
+        for(i=0;i<=0x18;i++) {
+            if (i != 0x11) {
+                outb(crtc_addr,i);
+                outb(crtc_addr+1, read_byte(ES, BX));
+            }
+            BX++;
+        }
+        // select crtc base address
+        v = inb(VGAREG_READ_MISC_OUTPUT) & ~0x01;
+        if (crtc_addr = 0x3d4)
+            v |= 0x01;
+        outb(VGAREG_WRITE_MISC_OUTPUT, v);
+
+        // enable write protection if needed
+        outb(crtc_addr, 0x11);
+        outb(crtc_addr+1, read_byte(ES, BX - 0x18 + 0x11));
+        
+        // Set Attribute Ctl
+        ar_index = read_byte(ES, addr1 + 0x03);
+        inb(VGAREG_ACTL_RESET);
+        for(i=0;i<=0x13;i++) {
+            outb(VGAREG_ACTL_ADDRESS, i | (ar_index & 0x20));
+            outb(VGAREG_ACTL_WRITE_DATA, read_byte(ES, BX)); BX++;
+        }
+        outb(VGAREG_ACTL_ADDRESS, ar_index);
+        inb(VGAREG_ACTL_RESET);
+        
+        for(i=0;i<=8;i++) {
+            outb(VGAREG_GRDC_ADDRESS,i);
+            outb(VGAREG_GRDC_DATA, read_byte(ES, BX)); BX++;
+        }
+        BX += 2; /* crtc_addr */
+        BX += 4; /* plane latches */
+        
+        outb(VGAREG_SEQU_ADDRESS, read_byte(ES, addr1)); addr1++;
+        outb(crtc_addr, read_byte(ES, addr1)); addr1++;
+        outb(VGAREG_GRDC_ADDRESS, read_byte(ES, addr1)); addr1++;
+        addr1++;
+        outb(crtc_addr - 0x4 + 0xa, read_byte(ES, addr1)); addr1++;
+    }
+    if (CX & 2) {
+        write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE, read_byte(ES, BX)); BX++;
+        write_word(BIOSMEM_SEG,BIOSMEM_NB_COLS, read_word(ES, BX)); BX += 2;
+        write_word(BIOSMEM_SEG,BIOSMEM_PAGE_SIZE, read_word(ES, BX)); BX += 2;
+        write_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS, read_word(ES, BX)); BX += 2;
+        write_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS, read_byte(ES, BX)); BX++;
+        write_word(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT, read_word(ES, BX)); BX += 2;
+        write_byte(BIOSMEM_SEG,BIOSMEM_VIDEO_CTL, read_byte(ES, BX)); BX++;
+        write_byte(BIOSMEM_SEG,BIOSMEM_SWITCHES, read_byte(ES, BX)); BX++;
+        write_byte(BIOSMEM_SEG,BIOSMEM_MODESET_CTL, read_byte(ES, BX)); BX++;
+        write_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE, read_word(ES, BX)); BX += 2;
+        for(i=0;i<8;i++) {
+            write_word(BIOSMEM_SEG, BIOSMEM_CURSOR_POS+2*i, read_word(ES, BX));
+            BX += 2;
+        }
+        write_word(BIOSMEM_SEG,BIOSMEM_CURRENT_START, read_word(ES, BX)); BX += 2;
+        write_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE, read_byte(ES, BX)); BX++;
+        /* current font */
+        write_word(0, 0x1f * 4, read_word(ES, BX)); BX += 2;
+        write_word(0, 0x1f * 4 + 2, read_word(ES, BX)); BX += 2;
+        write_word(0, 0x43 * 4, read_word(ES, BX)); BX += 2;
+        write_word(0, 0x43 * 4 + 2, read_word(ES, BX)); BX += 2;
+    }
+    if (CX & 4) {
+        BX++;
+        v = read_byte(ES, BX); BX++;
+        outb(VGAREG_PEL_MASK, read_byte(ES, BX)); BX++;
+        // Set the whole dac always, from 0
+        outb(VGAREG_DAC_WRITE_ADDRESS,0x00);
+        for(i=0;i<256*3;i++) {
+            outb(VGAREG_DAC_DATA, read_byte(ES, BX)); BX++;
+        }
+        BX++;
+        outb(VGAREG_DAC_WRITE_ADDRESS, v);
+    }
+    return BX;
+}
+
+// ============================================================================================
+//
+// Video Utils
+//
+// ============================================================================================
+// --------------------------------------------------------------------------------------------
+static Bit8u find_vga_entry(mode) 
+Bit8u mode;
+{
+ Bit8u i,line=0xFF;
+ for(i=0;i<=MODE_MAX;i++)
+  if(vga_modes[i].svgamode==mode)
+   {line=i;
+    break;
+   }
+ return line;
+}
+
+/* =========================================================== */
+/*
+ * Misc Utils
+*/
+/* =========================================================== */
+
+// --------------------------------------------------------------------------------------------
+static void memsetb(seg,offset,value,count)
+  Bit16u seg;
+  Bit16u offset;
+  Bit16u value;
+  Bit16u count;
+{
+ASM_START
+  push bp
+  mov  bp, sp
+
+    push ax
+    push cx
+    push es
+    push di
+
+    mov  cx, 10[bp] ; count
+    cmp  cx, #0x00
+    je   memsetb_end
+    mov  ax, 4[bp] ; segment
+    mov  es, ax
+    mov  ax, 6[bp] ; offset
+    mov  di, ax
+    mov  al, 8[bp] ; value
+    cld
+    rep
+     stosb
+
+memsetb_end:
+    pop di
+    pop es
+    pop cx
+    pop ax
+
+  pop bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+static void memsetw(seg,offset,value,count)
+  Bit16u seg;
+  Bit16u offset;
+  Bit16u value;
+  Bit16u count;
+{
+ASM_START
+  push bp
+  mov  bp, sp
+
+    push ax
+    push cx
+    push es
+    push di
+
+    mov  cx, 10[bp] ; count
+    cmp  cx, #0x00
+    je   memsetw_end
+    mov  ax, 4[bp] ; segment
+    mov  es, ax
+    mov  ax, 6[bp] ; offset
+    mov  di, ax
+    mov  ax, 8[bp] ; value
+    cld
+    rep
+     stosw
+
+memsetw_end:
+    pop di
+    pop es
+    pop cx
+    pop ax
+
+  pop bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+static void memcpyb(dseg,doffset,sseg,soffset,count)
+  Bit16u dseg;
+  Bit16u doffset;
+  Bit16u sseg;
+  Bit16u soffset;
+  Bit16u count;
+{
+ASM_START
+  push bp
+  mov  bp, sp
+
+    push ax
+    push cx
+    push es
+    push di
+    push ds
+    push si
+
+    mov  cx, 12[bp] ; count
+    cmp  cx, #0x0000
+    je   memcpyb_end
+    mov  ax, 4[bp] ; dsegment
+    mov  es, ax
+    mov  ax, 6[bp] ; doffset
+    mov  di, ax
+    mov  ax, 8[bp] ; ssegment
+    mov  ds, ax
+    mov  ax, 10[bp] ; soffset
+    mov  si, ax
+    cld
+    rep
+     movsb
+
+memcpyb_end:
+    pop si
+    pop ds
+    pop di
+    pop es
+    pop cx
+    pop ax
+
+  pop bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+static void memcpyw(dseg,doffset,sseg,soffset,count)
+  Bit16u dseg;
+  Bit16u doffset;
+  Bit16u sseg;
+  Bit16u soffset;
+  Bit16u count;
+{
+ASM_START
+  push bp
+  mov  bp, sp
+
+    push ax
+    push cx
+    push es
+    push di
+    push ds
+    push si
+
+    mov  cx, 12[bp] ; count
+    cmp  cx, #0x0000
+    je   memcpyw_end
+    mov  ax, 4[bp] ; dsegment
+    mov  es, ax
+    mov  ax, 6[bp] ; doffset
+    mov  di, ax
+    mov  ax, 8[bp] ; ssegment
+    mov  ds, ax
+    mov  ax, 10[bp] ; soffset
+    mov  si, ax
+    cld
+    rep
+     movsw
+
+memcpyw_end:
+    pop si
+    pop ds
+    pop di
+    pop es
+    pop cx
+    pop ax
+
+  pop bp
+ASM_END
+}
+
+/* =========================================================== */
+/*
+ * These functions where ripped from Kevin's rombios.c
+*/
+/* =========================================================== */
+
+// --------------------------------------------------------------------------------------------
+static Bit8u
+read_byte(seg, offset)
+  Bit16u seg;
+  Bit16u offset;
+{
+ASM_START
+  push bp
+  mov  bp, sp
+
+    push bx
+    push ds
+    mov  ax, 4[bp] ; segment
+    mov  ds, ax
+    mov  bx, 6[bp] ; offset
+    mov  al, [bx]
+    ;; al = return value (byte)
+    pop  ds
+    pop  bx
+
+  pop  bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+static Bit16u
+read_word(seg, offset)
+  Bit16u seg;
+  Bit16u offset;
+{
+ASM_START
+  push bp
+  mov  bp, sp
+
+    push bx
+    push ds
+    mov  ax, 4[bp] ; segment
+    mov  ds, ax
+    mov  bx, 6[bp] ; offset
+    mov  ax, [bx]
+    ;; ax = return value (word)
+    pop  ds
+    pop  bx
+
+  pop  bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+static void
+write_byte(seg, offset, data)
+  Bit16u seg;
+  Bit16u offset;
+  Bit8u  data;
+{
+ASM_START
+  push bp
+  mov  bp, sp
+
+    push ax
+    push bx
+    push ds
+    mov  ax, 4[bp] ; segment
+    mov  ds, ax
+    mov  bx, 6[bp] ; offset
+    mov  al, 8[bp] ; data byte
+    mov  [bx], al  ; write data byte
+    pop  ds
+    pop  bx
+    pop  ax
+
+  pop  bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+static void
+write_word(seg, offset, data)
+  Bit16u seg;
+  Bit16u offset;
+  Bit16u data;
+{
+ASM_START
+  push bp
+  mov  bp, sp
+
+    push ax
+    push bx
+    push ds
+    mov  ax, 4[bp] ; segment
+    mov  ds, ax
+    mov  bx, 6[bp] ; offset
+    mov  ax, 8[bp] ; data word
+    mov  [bx], ax  ; write data word
+    pop  ds
+    pop  bx
+    pop  ax
+
+  pop  bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+ Bit8u
+inb(port)
+  Bit16u port;
+{
+ASM_START
+  push bp
+  mov  bp, sp
+
+    push dx
+    mov  dx, 4[bp]
+    in   al, dx
+    pop  dx
+
+  pop  bp
+ASM_END
+}
+
+  Bit16u
+inw(port)
+  Bit16u port;
+{
+ASM_START
+  push bp
+  mov  bp, sp
+
+    push dx
+    mov  dx, 4[bp]
+    in   ax, dx
+    pop  dx
+
+  pop  bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+  void
+outb(port, val)
+  Bit16u port;
+  Bit8u  val;
+{
+ASM_START
+  push bp
+  mov  bp, sp
+
+    push ax
+    push dx
+    mov  dx, 4[bp]
+    mov  al, 6[bp]
+    out  dx, al
+    pop  dx
+    pop  ax
+
+  pop  bp
+ASM_END
+}
+
+// --------------------------------------------------------------------------------------------
+  void
+outw(port, val)
+  Bit16u port;
+  Bit16u  val;
+{
+ASM_START
+  push bp
+  mov  bp, sp
+
+    push ax
+    push dx
+    mov  dx, 4[bp]
+    mov  ax, 6[bp]
+    out  dx, ax
+    pop  dx
+    pop  ax
+
+  pop  bp
+ASM_END
+}
+
+Bit16u get_SS()
+{
+ASM_START
+  mov  ax, ss
+ASM_END
+}
+
+#ifdef DEBUG
+void unimplemented()
+{
+ printf("--> Unimplemented\n");
+}
+
+void unknown()
+{
+ printf("--> Unknown int10\n");
+}
+#endif
+
+// --------------------------------------------------------------------------------------------
+#if defined(USE_BX_INFO) || defined(DEBUG) || defined(CIRRUS_DEBUG)
+void printf(s)
+  Bit8u *s;
+{
+  Bit8u c, format_char;
+  Boolean  in_format;
+  unsigned format_width, i;
+  Bit16u  *arg_ptr;
+  Bit16u   arg_seg, arg, digit, nibble, shift_count;
+
+  arg_ptr = &s;
+  arg_seg = get_SS();
+
+  in_format = 0;
+  format_width = 0;
+
+  while (c = read_byte(0xc000, s)) {
+    if ( c == '%' ) {
+      in_format = 1;
+      format_width = 0;
+      }
+    else if (in_format) {
+      if ( (c>='0') && (c<='9') ) {
+        format_width = (format_width * 10) + (c - '0');
+        }
+      else if (c == 'x') {
+        arg_ptr++; // increment to next arg
+        arg = read_word(arg_seg, arg_ptr);
+        if (format_width == 0)
+          format_width = 4;
+        i = 0;
+        digit = format_width - 1;
+        for (i=0; i<format_width; i++) {
+          nibble = (arg >> (4 * digit)) & 0x000f;
+          if (nibble <= 9)
+            outb(0x0500, nibble + '0');
+          else
+            outb(0x0500, (nibble - 10) + 'A');
+          digit--;
+          }
+        in_format = 0;
+        }
+      //else if (c == 'd') {
+      //  in_format = 0;
+      //  }
+      }
+    else {
+      outb(0x0500, c);
+      }
+    s ++;
+    }
+}
+#endif
+
+ASM_START
+  ; get LFB address from PCI
+  ; in - ax: PCI device vendor
+  ; out - ax: LFB address (high 16 bit)
+  ;; NOTE - may be called in protected mode
+_pci_get_lfb_addr:
+  push bx
+  push cx
+  push dx
+  push eax
+    mov bx, ax
+    xor cx, cx
+    mov dl, #0x00
+    call pci_read_reg
+    cmp ax, #0xffff
+    jz pci_get_lfb_addr_fail
+ pci_get_lfb_addr_next_dev:
+    mov dl, #0x00
+    call pci_read_reg
+    cmp ax, bx ;; check vendor
+    jz pci_get_lfb_addr_found
+    add cx, #0x8
+    cmp cx, #0x200 ;; search bus #0 and #1
+    jb pci_get_lfb_addr_next_dev
+ pci_get_lfb_addr_fail:
+    xor dx, dx ;; no LFB
+    jmp pci_get_lfb_addr_return
+ pci_get_lfb_addr_found:
+    mov dl, #0x10 ;; I/O space #0
+    call pci_read_reg
+    test ax, #0xfff1
+    jz pci_get_lfb_addr_success
+    mov dl, #0x14 ;; I/O space #1
+    call pci_read_reg
+    test ax, #0xfff1
+    jnz pci_get_lfb_addr_fail
+ pci_get_lfb_addr_success:
+    shr eax, #16
+    mov dx, ax ;; LFB address
+ pci_get_lfb_addr_return:
+  pop eax
+  mov ax, dx
+  pop dx
+  pop cx
+  pop bx
+  ret
+
+  ; read PCI register
+  ; in - cx: device/function
+  ; in - dl: register
+  ; out - eax: value
+pci_read_reg:
+  mov eax, #0x00800000
+  mov ax, cx
+  shl eax, #8
+  mov al, dl
+  mov dx, #0xcf8
+  out dx, eax
+  add dl, #4
+  in  eax, dx
+  ret
+ASM_END
+
+#ifdef VBE
+#include "vbe.c"
+#endif
+
+#ifdef CIRRUS
+#include "clext.c"
+#endif
+
+// --------------------------------------------------------------------------------------------
+
+ASM_START 
+;; DATA_SEG_DEFS_HERE
+ASM_END
+
+ASM_START
+.ascii "vgabios ends here"
+.byte  0x00
+vgabios_end:
+.byte 0xCB
+;; BLOCK_STRINGS_BEGIN
+ASM_END