
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <conio.h>
#include <dos.h>


typedef unsigned char byte;

static byte fontbuf[4096];

#define VID_A_MDA 0
#define VID_A_CGA 1
#define VID_A_EGA 2
#define VID_A_VGA 3

byte vid_type()
{
	union REGS inregs,outregs;
/*
    Video adaptor type determination:

    Set AX=1200, BL=32 and call INT 10.
*/
	inregs.x.ax=0x1200;
	inregs.h.bl=0x32;
	int86(0x10,&inregs,&outregs);

/*  If AL returns 12, you have a VGA.  If not, set AH=12, BL=10 and call 
    INT 10 again. */

    	if (outregs.h.al==0x12) return VID_A_VGA;
    	inregs.h.ah=0x12;
    	inregs.h.bl=0x10;
	int86(0x10,&inregs,&outregs);

/* If BL returns 0,1,2,3, you have an EGA with 64,128,192,256K memory.  
   If not, set AH=0F and call INT 10 a third time.  */

	if (outregs.h.bl < 4) return VID_A_EGA;

   	inregs.h.ah=0x0F;
   	int86(0x10,&inregs,&outregs);
/*   	
   If AL is 7, you have an MDA (original monochrome adapter) or Hercules; 
   if not, you have a CGA.
 */
 	if (outregs.h.al == 7) return VID_A_MDA;
 	return VID_A_CGA;
}


static byte old_mode;

#define save_mode() old_mode = get_mode()

byte get_mode()
{	
	union REGS inregs,outregs;

	inregs.h.ah=0x0f;
	int86(0x10,&inregs,&outregs);
	return outregs.h.al;	
}

void set_mode(byte m)
{
	union REGS inregs,outregs;

	inregs.h.ah=0x00;
	inregs.h.al=m;
	int86(0x10,&inregs,&outregs);
}

#define restore_mode() set_mode(old_mode)

int v_set_mode(byte m)
{
	byte m1;

	set_mode(m);
	if ((m1 = get_mode()) == m) return 1;

	restore_mode();
	fprintf(stderr, "Asked for video mode %d but got %d\n", m, m1);
	fprintf(stderr, "Press RETURN...\n");
	getch();
	return 0;
}

void draw_char(int ch)
{
	union REGS inregs, outregs;

	inregs.h.ah = 0x09;
	inregs.h.al = ch;
	inregs.h.bh = 0;
	inregs.h.bl = 0x0F;
	inregs.x.cx = 1;
	int86(0x10, &inregs, &outregs);
}

void emit_psf(char *f, int height)
{
	char fname[20];
	FILE *fp;

	sprintf(fname, "%s%02d.psf", f, height);
	fp = fopen(fname, "wb");
	if (!fp) 
	{
		restore_mode();
		perror(fname);	
		exit(1);
	}
	fputc(0x36, fp);
	fputc(0x04, fp);
	fputc(0x00, fp);
	fputc(height, fp);
	fwrite(fontbuf, height, 256, fp);
	fclose(fp);
}


void save_font_16(char *filename)
{
	int ch, n;
	byte far *screen = MK_FP(0xA000, 0);
	byte *target;

	if (!v_set_mode(0x11)) return;
	for (ch = 0; ch < 256; ch++)
	{
		draw_char(ch);				
		target = fontbuf + 16 * ch;
		for (n = 0; n < 16; n++)
		{
			*target++ = screen[n * 80];
		}
	}
	emit_psf(filename, 16);
}


void save_font_14(char *filename)
{
	int ch, n;
	byte far *screen = MK_FP(0xA000, 0);
	byte *target;

	if (!v_set_mode(0x0F)) return;
	for (ch = 0; ch < 256; ch++)
	{
		draw_char(ch);				
		target = fontbuf + 14 * ch;
		for (n = 0; n < 14; n++)
		{
			*target++ = screen[n * 80];
		}
	}
	emit_psf(filename, 14);
}



void save_font_8(char *filename)
{
	int ch, n;
	byte far *screen = MK_FP(0xB800, 0);
	byte *target;

	if (!v_set_mode(6)) return;
	for (ch = 0; ch < 256; ch++)
	{
		draw_char(ch);				
		target = fontbuf + 8 * ch;
		for (n = 0; n < 4; n++)
		{
			*target++ = screen[n * 80];
			*target++ = screen[n * 80 + 0x2000]; 
		}
	}
	emit_psf(filename, 8);
}



int main(int argc, char **argv)
{
	char fname[7];

	if (argc > 1) 
	{
		strncpy(fname, argv[1], 6);
		fname[6] = 0;
	}
	else	strcpy(fname, "font");

	save_mode();

	switch(vid_type())
	{
		case VID_A_VGA:
			save_font_16(fname);			
			/* And fall through to CGA */
		case VID_A_EGA: 
			save_font_14(fname);			
			/* And fall through to CGA */
		case VID_A_CGA:
			save_font_8(fname);			
			break;
		case VID_A_MDA:	      /* 1...5...10...15...20...25...30...35...40 */
			fprintf(stderr, "This computer appears to have a Monochro"
					"me Display Adapter. The character\n"
					"patterns for this adapter are not visibl"
					"e to DOS programs.\n\n");
			exit(1);
	}	
	restore_mode();
	return 0;
}

