text: /*         ______   ___    ___ 
   *        /\  _  \ /\_ \  /\_ \ 
   *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___ 
   *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
   *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
   *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
   *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
   *                                           /\____/
   *                                           \_/__/
   *
   *      VGA helper functions for Linux Allegro.
   *
   *      Originally by Marek Habersack, mangled by George Foot.
   *
   *      See readme.txt for copyright information.
   */
  
  #ifndef _GNU_SOURCE
  #define _GNU_SOURCE
  #endif
  
  #include "allegro.h"
  
  #ifdef ALLEGRO_LINUX_VGA
  
  #include "allegro/internal/aintern.h"
  #include "allegro/platform/aintunix.h"
  #include "allegro/internal/aintvga.h"
  #include "linalleg.h"
  
  #include <string.h>
  #include <stdio.h>
  #include <signal.h>
  #include <termios.h>
  #include <sys/ioctl.h>
  #include <linux/vt.h>
  #include <linux/kd.h>
  #include <sys/mman.h>
  
  
  /* Used by the text font routines, to access the video memory */
  static struct MAPPED_MEMORY vram = {
  	VGA_MEMORY_BASE, VGA_MEMORY_SIZE,
  	PROT_READ | PROT_WRITE,
  	NULL
  };
  
  
  int __al_linux_init_vga_helpers (void)
  {
     __al_linux_map_memory (&vram);
     _vga_regs_init();
     return 0;
  }
  
  int __al_linux_shutdown_vga_helpers (void)
  {
     __al_linux_unmap_memory (&vram);
     return 0;
  }
  
  /* __al_linux_set_vga_regs:
   *  Write the standard VGA registers using values from the passed structure.
   */
  void __al_linux_set_vga_regs(MODE_REGISTERS *regs)
  {
     int i = 0;
  
     if (!regs)
        return;
     
     outportb(MISC_REG_W, regs->misc);
  
     outportw(SEQ_REG_I, 0x0100);
     for (i = 1; i < N_SEQ_REGS; i++)
        outportw(SEQ_REG_I, (regs->seq[i] << 8) | i);
     outportw(SEQ_REG_I, 0x0300);
  
     outportw(_crtc, ((_read_vga_register(_crtc, 0x11) & 0x7F) << 8) | 0x11);
     for (i = 0; i < N_CRTC_REGS; i++)
        outportw(_crtc, (regs->crt[i] << 8) | i);
  
     for (i = 0; i < N_GC_REGS; i++)
        outportw(GC_REG_I, (regs->gc[i] << 8) | i);
  
     for (i = 0; i < N_ATC_REGS; i++) {
        inportb(_is1);
        outportb(ATC_REG_IW, i);
        outportb(ATC_REG_IW, regs->atc[i]);
        __just_a_moment();
     }
  }
  
  /* __al_linux_get_vga_regs:
   *  Read and save in the passed array all standard VGA regs for the current
   *  video mode.
   */
  void __al_linux_get_vga_regs(MODE_REGISTERS *regs)
  {
     int i = 0;
  
     if (!regs)
        return;
     
     regs->misc = inportb(MISC_REG_R);
     
     for (i = 0; i < N_CRTC_REGS; i++) {
        outportb(_crtc, i);
        regs->crt[i] = inportb(_crtc+1);
     }
  
     for (i = 0; i < N_GC_REGS; i++) {
        outportb(GC_REG_I, i);
        regs->gc[i] = inportb(GC_REG_RW);
     }
  
     for (i = 0; i < N_SEQ_REGS; i++) {
        outportb(SEQ_REG_I, i);
        regs->seq[i] = inportb(SEQ_REG_RW);
     }
  
     for (i = 0; i < N_ATC_REGS; i++) {
        inportb(_is1);
        outportb(ATC_REG_IW, i);
        regs->atc[i] = inportb(ATC_REG_R);
        __just_a_moment();
     }
  }
  
  
  static void __al_linux_save_palette (MODE_REGISTERS *regs)
  {
     int c;
     unsigned char *ptr;
     ASSERT(regs);
  
     ptr = regs->palette.vga;
  
     for (c = 0; c < 256; c++) {
        outportb (0x3c7, c);
        *ptr++ = inportb (0x3c9);
        *ptr++ = inportb (0x3c9);
        *ptr++ = inportb (0x3c9);
     }
  }
  
  static void __al_linux_restore_palette (MODE_REGISTERS *regs)
  {
     int c;
     unsigned char *ptr;
     ASSERT(regs);
  
     ptr = regs->palette.vga;
  
     for (c = 0; c < 256; c++) {
        outportb (0x3c8, c);
        outportb (0x3c9, *ptr++);
        outportb (0x3c9, *ptr++);
        outportb (0x3c9, *ptr++);
     }
  }
  
  
  static inline void slow_byte_copy(char *from, char *to, unsigned count)
  {
     unsigned int i;
     ASSERT(from && to);
  
     for (i = 0; i < count; i++) {
        *to++ = *from++;
        outportb(0x80, 0x00);  /* 1ms delay */
     }
  }
  
  /* __al_linux_save_text_font:
   *  Copy the text font data from the video memory.  Since some cards won't work
   *  properly unless we save/restore fonts from both 2nd and 3rd plane, we save
   *  them both.
   */
  static void __al_linux_save_text_font (MODE_REGISTERS *regs)
  {
     ASSERT(regs);
  
     if (!regs->text_font1)
        regs->text_font1 = (unsigned char*) _AL_MALLOC (VGA_FONT_SIZE);
     if (!regs->text_font2)
        regs->text_font2 = (unsigned char*) _AL_MALLOC (VGA_FONT_SIZE);
  
     /* First switch to a 4bpp video mode, so that we have selective 
      * access to video memory planes.  Do it with host palette 
      * access disabled. */
     inportb(_is1);
     outportb(ATC_REG_IW, 0x30);
     outportb(ATC_REG_IW, 0x01);   /* Put ATC into graphics mode */
     outportw(SEQ_REG_I, 0x0604);  /* Planar mode */
     outportw(GC_REG_I, 0x0005);   /* Write Mode == Read Mode == 0 */
     outportw(GC_REG_I, 0x0506);   /* Switch GC to graphics mode and start VGA
                                    * memory at 0xA0000 (64KB) */
     
     /* Now we're ready to read the first font.  We'll do it using the slowest
      * copy routine possible.  It is to account for the old VGA boards which may
      * lose data during too fast reads/writes.
      */
     outportw(GC_REG_I, 0x0204); /* Reading from plane 2 */
     slow_byte_copy ((char*)vram.data, (char*)regs->text_font1, VGA_FONT_SIZE);
     outportw(GC_REG_I, 0x0304); /* Reading from plane 3 */
     slow_byte_copy ((char*)vram.data, (char*)regs->text_font2, VGA_FONT_SIZE);
  }
  
  /* __al_linux_restore_text_font:
   *  Copy the saved font data back to video memory.
   */
  static void __al_linux_restore_text_font(MODE_REGISTERS *regs)
  {
     ASSERT(regs);
  
     /* Same as in the previous routing.  We're restoring the font in a 
      * 4bpp mode to have access to separate memory planes. */
     inportb(_is1);
     outportb(ATC_REG_IW, 0x30);
     outportb(ATC_REG_IW, 0x01);   /* Put ATC into graphics mode */
     outportw(SEQ_REG_I, 0x0604);  /* Planar mode */
     outportw(GC_REG_I, 0x0005);   /* Write Mode == Read Mode == 0 */
     outportw(GC_REG_I, 0x0506);   /* Switch GC to graphics mode and start VGA
                                    * memory at 0xA0000 (64KB) */
     outportw(GC_REG_I, 0x0001);
  
     if (regs->text_font1) {
        outportw(SEQ_REG_I, 0x0402);
        slow_byte_copy((char*)regs->text_font1, (char*)vram.data, VGA_FONT_SIZE);
     }
  
     if (regs->text_font2) {
        outportw(SEQ_REG_I, 0x0802);
        slow_byte_copy((char*)regs->text_font2, (char*)vram.data, VGA_FONT_SIZE);
     }
  }
  
  
  void __al_linux_screen_off(void)
  {
     _write_vga_register(SEQ_REG_I, 0x01,
                         _read_vga_register(SEQ_REG_I, 0x01) | 0x20);
     inportb(_is1);
     __just_a_moment();
     outportb(ATC_REG_IW, 0x00);
     __just_a_moment();
  }
  
  void __al_linux_screen_on(void)
  {
     _write_vga_register(SEQ_REG_I, 0x01,
                         _read_vga_register(SEQ_REG_I, 0x01) & 0xDF);
     inportb(_is1);
     __just_a_moment();
     outportb(ATC_REG_IW, 0x20);
     __just_a_moment();
  }
  
  void __al_linux_clear_vram (void)
  {
     memset (vram.data, 0, vram.size);
  }
  
  
  static MODE_REGISTERS txt_regs;
  
  void __al_linux_save_text_mode (void)
  {
     __al_linux_get_vga_regs (&txt_regs);
     __al_linux_save_palette (&txt_regs);
     __al_linux_save_text_font (&txt_regs);
  }
  
  void __al_linux_restore_text_mode (void)
  {
     __al_linux_restore_text_font (&txt_regs);
     __al_linux_restore_palette (&txt_regs);
     __al_linux_set_vga_regs (&txt_regs);
  }
  
  #endif /* ALLEGRO_LINUX_VGA */
  