www.delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp/1994/08/16/20:27:04

Date: Tue, 16 Aug 1994 13:36:03 -0700 (PDT)
From: Gordon Hogenson <ghogenso AT u DOT washington DOT edu>
Subject: Re: Fonts and DJGPP
To: Tom Roden <tom AT batc DOT allied DOT com>
Cc: djgpp AT sun DOT soe DOT clarkson DOT edu, tom AT batc DOT allied DOT com

On Tue, 16 Aug 1994, Tom Roden wrote:

> Greetings,
> 
> Does anyone out there know how to change fonts in VGA text mode from within a
> program?  What I would like to do is allow the program user to select a 
> new font and then load that font.  I am not picky about the font format,
> X fonts, windows fixed fonts or bitmapped images are ok with me, I would
> just like to find out how to do it.  Either a description or, better yet, 
> an example code fragment would be greatly appreciated.
> 
> Thanks in advance for any help,
> 
> Regards,
> 
> Tom Roden
> tom AT batc DOT allied DOT com
> 
> 

You might find the following code useful.  It contains functions
for loading bitmapped fonts into video memory.  These functions
assume that you already have fonts in the format in which they
occur in video memory, namely, 32-bytes per character.  A whole
font would be declared as

char myfont[32 * 256 ];

Limitations are that these deal only with 8xN character dimensions.
I don't know how to handle the 9th bit of 9xN character cells.  Also,
I didn't provide a function that sets the height of the characters.
No reason, really, it's not difficult, I just didn't need it for
the program this comes from.  Also, the code is not designed for speed
or efficiency (for example, the BIOS is used for setting the palette), 
but it works (I think).


--------------------------------------------------------
charset.h
--------------------------------------------------------
/* charset.h
 * The code in this file is donated to the public domain
 * by Gordon Hogenson, 1994
 *
 * Utility functions for VGA fonts [and palette]
 */

#ifndef CHARSET_H
#define CHARSET_H

#include <go32.h>  /* for go32 info block, dosmemget, dosmemput */
#include <dos.h>   /* for outportb, inportb */
#include <dpmi.h>  /* for the interrupts that use _go32_dpmi_* */


typedef unsigned char byte, BYTE;
typedef unsigned short word, WORD;

enum bit { D0 = 1,
                 D1 = 2,
                 D2 = 4,
                 D3 = 8,
                 D4 = 0x10,
                 D5 = 0x20,
                 D6 = 0x40,
                 D7 = 0x80
}               


#ifdef __cplusplus
extern "C" {
#endif

void read_char_gen(byte* buf, unsigned short k,
                  unsigned short offset, int font_num);

void write_char_gen(const byte* buf, unsigned short k,
                        unsigned short offset, int font_num);

void enable_512_set();
void disable_512_set();
void get_active_character_sets(int *i);
void set_active_character_sets(int *i);
int lines_per_char();

void write_palette(const byte* table);
void read_palette(byte* table);

void blink_mode(int i);

#ifdef __cplusplus
}
#endif

#endif /* ifdef CHARSET_H */

----------------------------------------------------------
charset.c
-----------------------------------------------------------
/* charset.c
 * this code is donated to the public domain
 * by Gordon Hogenson, 1994
 */

#include "charset.h"
#include <stdio.h>
#include <assert.h>
#include <string.h>

/* These variables correspond to the VGA registers.
   seq = sequencer.  gc = graphics controller. */
 
static byte seq_active_color_planes;
static byte seq_mem_mode;
static byte gc_select_mode;
static byte gc_misc;
static byte gc_read_map_select;

/* enable_plane2 is called by the write_char_gen
   routine in order to write to video memory plane 2 */

static void enable_plane2()
{
 outportb(0x3C4, 2);
  seq_active_color_planes = inportb(0x3C5);

  outportb(0x3C4, 4);
  seq_mem_mode = inportb(0x3C5);

  outportb(0x3CE, 5);
  gc_select_mode = inportb(0x3CF);

  outportb(0x3CE, 6);
  gc_misc = inportb(0x3CF);
  
  outportb(0x3ce, 4);
  gc_read_map_select = inportb(0x3cf);
 
  /* Sequencer Index 2: Color Plane enable register.
   * set to enable only color plane 2, which contains
   * the character font data
   */
   
  outportb(0x3c4, 2);
  outportb(0x3c5, 4);

  /* Sequencer Index 4: Memory Mode register
   * set the odd/even bit to one
   */
   
  outportb(0x3C4, 4);
  outportb(0x3c5, 6);    

  /* Graphics controller Index 5: Mode Register
   * disable odd/even chaining
   */
   
  outportb(0x3CE, 5);
  outportb(0x3cf, 0);  

  /* Graphics controller Index 6: Misc Register
   * enable addressing at A000, disable odd/even mode
   */
   
  outportb(0x3CE, 6);
  outportb(0x3cf, 5);

  outportb(0x3ce, 4);   /* select read map select register */
  outportb(0x3cf, 2);   /* enable plane 2 for reading */
 

}

static void disable_plane2()
{

   outportb(0x3C4, 2);  /* enable plane 0 & 1 */
   outportb(0x3C5, seq_active_color_planes);

   outportb(0x3C4, 4);  /* even/odd */
   outportb(0x3C5, seq_mem_mode);

   outportb(0x3CE, 5);
   outportb(0x3CF, gc_select_mode);

   outportb(0x3CE, 6);
   outportb(0x3CF, gc_misc);

   outportb(0x3ce, 4);
   outportb(0x3cf, gc_read_map_select);
}

static int base_font_addr(int font_num)
{
  /* the fonts are all 8192 bytes in length (256*32).
   * they are arranged in the following order in
   * video memory: 0 4 1 5 2 6 3 7.
   * This computes the base address given the number
   * (i.e., 0 - 8) of the font.
   */
        
  int font_addr;
  
  if (font_num >= 0 && font_num < 4)
     {
                font_addr = 16384*font_num;
     }
   else
     {
       if (font_num >=4 && font_num < 8)
         {
          font_addr = 8192 + 16384*(font_num - 4);
         }
       else
         {
            fprintf(stderr,"Invalid font number %d.", font_num);
         }
     }
  return font_addr;
}

/* buf is a ptr to k 32-byte blocks, representing characters.
 * this procedure reads k character bitmaps (starting at
 * an offset into the character table specified by "offset")
 * into "buf"
 *
 * for example:
 *
 * char mybuf[26*32];
 * read_char_gen(mybuf, 26, 'A', 1);
 *
 * could be used to read the 26 upper-case letters, starting at
 * 'A' from font 1.
 */

void read_char_gen(byte* buf, word k, word offset, int font_num)
{
 
  /* Memory to  be modified is in Segment A000,
   * offset = (bytes per char) * (offset into table )
   *   + (font_addr)
   */
  
  enable_plane2();   
  dosmemget( 0xA000 * 16 + base_font_addr(font_num)
                         + 32*offset, 32*k, buf );
  disable_plane2();
     
}

/* The arguments to this function are the same as those
 * above, except buf is now an input.  Thus:
 *
 * char mybuf[26*32];
 * write_char_gen(mybuf, 26, 'A', 0);
 * 
 * writes the bitmaps contained in mybuf into font 0 (the default font).
 * write_char_gen does not change which character sets are
 * active.  You need to call enable_512_set() if you are
 * just using fonts 0 and 1, or set_active_fonts(int *) to
 * specify any two fonts to be active.
 * This might work in EGA, if you limit yourself to fonts 0 and 1.
 */

void write_char_gen(const byte* buf, word k, word offset, int font_num)
{
   /* Memory to  be modified is in Segment A000,
   * offset = (bytes per char) * (offset into table )
   *   + font base address
   */

  enable_plane2();
  dosmemput(buf, 32*k, 0xA000 * 16 + base_font_addr(font_num)
                         + 32*offset );
  disable_plane2();
}

/* enable_512_set() assumes you have already loaded
   the set into character set 2 using write_char_gen.
*/

static byte save_buf[17];

void enable_512_set()
{
   byte buf[17];
   
   read_palette(save_buf);     /* the purpose of these four lines */
   memcpy(buf, save_buf, 17);  /* is to disable bit D3 of the attribute */
   memcpy(buf+8, save_buf, 8); /* byte from affecting color, so that */
   write_palette(buf);         /* colors from both fonts are the same */
        
        /* select the CHAR SET SELECT register */
   outportb(0x3c4, 3);
     /* select the 2nd character set to be active */
   outportb(0x3c5, 4); 
}

/* disable bit D3 of the attribute byte from specifying the
 * alternate (512) character set.
 */

void disable_512_set()
{
    byte buf[17];
   
   memcpy(buf, save_buf, 17);
   write_palette(buf);
        
        
        outportb(0x3c4, 3);
        outportb(0x3c5, 0);
}

/* get which of the character sets (0-7) is active.
 * argument must be able to hold two integers
 */

void get_active_character_sets(int *i)
{
  byte b;
 
  outportb(0x3c4, 3);
  b = inportb(0x3c5);
 /* fprintf(stderr,"seq_active_sets =  %x \n", b); */
  
  i[0] = ((b & D4) >> 2) | ( b & (D1 | D0) );
  i[1] = ((b & D5) >> 3) | ( ( b & (D2 | D3) ) >> 2);
}

/* select which of the eight character sets (0-7) are active
   the input is a 2-element array of ints containing 0-7 in each
   slot, where 0-7 specifies the font. */

void set_active_character_sets(int *i)
{
        int set1 = i[0];
        int set2 = i[1];

   assert( set1 >= 0 && set1 < 8);
   assert( set2 >= 0 && set2 < 8);

   set1 = ((set1 & D2) << 2 ) | (set1 & (D1 | D0));
   set2 = ((set2 & D2) << 3 ) | ((set2 & (D1 | D0)) << 2);
   
        /* select the CHAR SET SELECT register */
   outportb(0x3c4, 3);
   outportb(0x3c5, set1 | set2);        
}

/* return the number of scan lines in the current font */

int lines_per_char()
{
   byte crt_text_char_height;
   
   outportb(0x3d4, 9);
   crt_text_char_height = inportb(0x3d5);

   return (crt_text_char_height & 0x1F) + 1;

}

/*
  This function calls BIOS INT 10, function 0x10, subfunction 2
  to set the palette to the table pointed to by the input pointer.
  The input pointer should be 17-bytes long, the 17th byte being
  the overscan color.

  These could be made more efficient by circumventing the BIOS
  and writing to the registers directly.
*/

void write_palette(const byte* table)
{
        _go32_dpmi_registers r;
        unsigned long xfer_buf_addr =
                _go32_info_block.linear_address_of_transfer_buffer;
        word xfer_buf_offset =
                (word)(xfer_buf_addr % 16 );       
        word xfer_buf_seg =
                (word)(xfer_buf_addr / 16 );
        dosmemput(table, 17, xfer_buf_addr);
        memset(&r, 0, sizeof(_go32_dpmi_registers));
        r.x.es = xfer_buf_seg;
        r.x.dx = xfer_buf_offset;
        r.x.ax = 0x1002;
        _go32_dpmi_simulate_int(0x10, &r);
}

/* This calls BIOS INT 10, function 0x10,
   subfunction 0x9 to read the EGA/VGA palette.  
   Pass a pointer to a 17-byte table to hold the
   return value.  The value in table[16] is the
   overscan color.
*/

void read_palette(byte *table)
{
        _go32_dpmi_registers r;
        unsigned long xfer_buf_addr =
                _go32_info_block.linear_address_of_transfer_buffer;
        word xfer_buf_offset =
                (word)(xfer_buf_addr % 16 );       
        word xfer_buf_seg =
                (word)(xfer_buf_addr / 16 );
        memset(&r, 0, sizeof(_go32_dpmi_registers)); 
        r.x.es = xfer_buf_seg;
        r.x.dx = xfer_buf_offset;
        r.x.ax = 0x1009;
        _go32_dpmi_simulate_int(0x10, &r);
        dosmemget(xfer_buf_addr, 17, table);
}


void blink_mode(int i)
{
        union REGS r;
       
        r.x.ax = 0x1003;
        r.h.bl = i ? 1 : 0;
        int86(0x10, &r, &r);
}



-------------------------------------------------------------------

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019