1 /*
2     psftools: Manipulate console fonts in the .PSF format
3     Copyright (C) 2005-6  John Elliott
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 #include "cnvshell.h"
20 #include "psflib.h"
21 #include "dos.h"
22 #include "intrpt.h"
23 
24 /* Set this to 1 to enable custom Herc mode settings (see Richard Wilton's
25  * "Programmer's Guide to PC & PS/2 Video Systems, p327) allowing character
26  * sizes other than 9x14
27  *
28  * This is turned off because when I tried it, it didn't work very well.
29  * Fonts 8 pixels wide (ie, nearly all of them) put the screen in a 90-column
30  * mode that went off the right-hand side; an 8x14 font selected a 90x25
31  * mode that was too big to fit in available video RAM; and the smaller
32  * sizes caused my MDA monitor to lose sync.
33  */
34 #define HERC_CUSTOM	0
35 
36 
37 /* XXX Add support for the 3270PC Programmed Symbols board if none of the
38  * other graphics cards is present */
39 char *herc_loadfont (psf_byte far *buffer, int count, int width, int height,
40 		int map);
41 char *ega_loadfont (psf_byte far *buffer, int count, int height, int map);
42 char *conv_loadfont(psf_byte far *buffer, int count, int height, int map);
43 char *apricot_loadfont(psf_byte far *buffer, int count, int width, int height,
44 		int slot);
45 char *wang_loadfont(psf_byte far *buffer, int count, int width, int height,
46 		int slot);
47 static char *apricot_pc_loadfont(psf_byte far *buffer, int count,
48 		int width, int height, int map);
49 static char *apricot_f_loadfont(psf_byte far *buffer, int count,
50 		int height, int map);
51 static char *wang_mono_loadfont(psf_byte far *buffer, int count, int width,
52 		int height);
53 static char *wang_colour_loadfont(psf_byte far *buffer, int count, int width,
54 		int height);
55 /*
56 static char *wang_cgdc_loadfont(psf_byte far *buffer, int count, int width,
57 		int height);
58 */
59 static int wang_active_display_type(void);
60 
61 static int convertible = 0;
62 static int ega = 0;
63 static int herc = 0;
64 static int apricot = 0;
65 static int wang = 0;
66 
67 static int herc_probe(void);
68 
video_probe(void)69 char *video_probe(void)
70 {
71 	struct SREGS sg;
72 	union REGS rg;
73 	unsigned char far *ptr;
74 
75 	/* Check for Apricot type PCs. This must be done first because
76 	 * the Apricot doesn't have to have an INT 10h */
77 	ptr = MK_FP(0x40, 0);
78 	if (ptr[4] == 0x34 && ptr[5] == 0x12 &&
79 	    ptr[6] == 0x78 && ptr[7] == 0x56 &&
80 	    ptr[1] < 3)
81 	{
82 		apricot = 1 + ptr[1];
83 		return NULL;
84 	}
85 	/* Check for Wang type PCs, to which the same caveat applies.
86 	 * Unfortunately, I don't think there's a fixed signature for a
87 	 * Wang BIOS in the same way that there is for an Apricot BIOS, so
88 	 * the method is a little more arbitrary: see if there's a jump
89 	 * at 0040:0000, and check that the segment of the INT 7E handler
90 	 * is 0040h. */
91 	ptr = MK_FP(0, 0);
92 	if (ptr[0x400] == 0xEB && ptr[0x401] == 2 && ptr[0x404] == 0xFA &&
93 	    ptr[0x1FA] == 0x40 && ptr[0x1FB] == 0x00)
94 	{
95 		/* Now detect video type */
96 		int slot;
97 
98 		for (wang = 0, slot = 1; slot < 15; slot++)
99 		{
100 			switch(inportb(0x10FE + 256 * slot) & 0x7F)
101 			{
102 				case 0x10: wang |= 1; break;
103 				case 0x11:
104 				case 0x15: wang |= 2; break;
105 // The CGDC display class is not supported yet
106 				case 0x13:
107 				case 0x17: /* wang |= 4; */ break;
108 			}
109 		}
110 		if (!wang)
111 		{
112 			return "Wang MS-DOS detected, but no suitable "
113 				"displays found.";
114 		}
115 		return NULL;
116 	}
117 
118 
119 	/* Check for IBM Convertible */
120 	rg.h.ah = 0xC0;
121 	int86x(0x15, &rg, &rg, &sg);
122 	if (rg.h.ah == 0)
123 	{
124 		ptr = MK_FP(sg.es, rg.x.bx);
125 		if (ptr[2] == 0xF9)
126 		{
127 			convertible = 1;
128 			return 0;
129 		}
130 		if (ptr[2] == 0xFC)	/* AT-class */
131 		{
132 			/* Check for Compaq portable III / 386 */
133 			union REGS rg;
134 
135 			ptr = MK_FP(0xF000, 0xFFEA);
136 			if (ptr[0] == 'C' && ptr[1] == 'O' && ptr[2] == 'M'
137 			&&  ptr[3] == 'P' && ptr[4] == 'A' && ptr[5] == 'Q')
138 			{
139 /* Get Compaq environment */
140 				rg.x.ax = 0xbf03;
141 				rg.x.bx = 0;
142 				rg.x.cx = 0;
143 				rg.x.dx = 0;
144 				int86(0x10, &rg, &rg);
145 /* If Compaq environment present and internal monitor is a 640x400 flat panel,
146  * this is presumed to be a Compaq Portable III / 386 */
147 				if (rg.x.bx != 0 && rg.h.dh == 4)
148 				{
149 					convertible = 2;
150 					return 0;
151 				}
152 			}
153 		}
154 
155 	}
156 	/* Check for EGA/VGA */
157 	rg.h.ah = 0x12;
158 	rg.x.bx = 0xFF10;
159 	int86(0x10, &rg, &rg);
160 	if (rg.h.bh != 0xFF)
161 	{
162 		ega = 1;
163 		return 0;
164 	}
165 	/* Check for Hercules Plus */
166 	if (herc_probe())
167 	{
168 		herc = 1;
169 		return 0;
170 	}
171 	      //1...5...10...15...20...25...30...35...40...45...50...55
172 	return "No supported video hardware detected. Must be one of:\n"
173 	       "* EGA/VGA\n"
174 	       "* IBM Convertible\n"
175 	       "* Hercules Plus\n"
176 	       "* Apricot PC, Xi or F-Series\n"
177 	       "* Wang Professional Computer\n";
178 }
179 
valid_font(PSF_FILE * psf)180 char *valid_font(PSF_FILE *psf)
181 {
182 	if (herc)
183 	{
184 #ifdef HERC_CUSTOM
185 		if (psf->psf_height < 4 || psf->psf_height > 16)
186 			return "Hercules Plus only supports fonts "
187 				"4-16 pixels high.";
188 		if (psf->psf_width > 9)
189 			return "Hardware does not support fonts wider than "
190 				"9 pixels.";
191 #else
192 		if (psf->psf_height != 14 || psf->psf_width != 8)
193 		{
194 			return "Fonts for Hercules Plus must be 8 pixels wide "
195 				"and 14 high.";
196 		}
197 #endif
198 	}
199 	else if (apricot)
200 	{
201 		unsigned char far *ptr = MK_FP(0x70, 0);
202 		if (apricot == 1)	/* PC/Xi */
203 		{
204 			if (psf->psf_width > 10 || psf->psf_height > 16)
205 				return "Apricot PC/Xi fonts can be at most "
206 					"10x16 in size";
207 
208 		}
209 		else
210 		{
211 			unsigned allowed = 0;
212 
213 			if (psf->psf_width > 8)
214 				return "Hardware does not support "
215 				"fonts wider than 8 pixels.";
216 			if (psf->psf_height == 8) allowed = 1;
217 			if (psf->psf_height == 10 && ptr[0x11] >= 0x12)
218 				allowed = 1;
219 			if (!allowed)
220 			{
221 				if (ptr[0x11] < 0x12)
222 					return "Only 8x8 fonts can be loaded";
223 				return "Only 8x8 and 8x10 fonts can be loaded";
224 			}
225 		}
226 	}
227 	else 	if (wang)
228 	{
229 		if (wang & 2)	/* Do we have a mono display? */
230 		{
231 			if (psf->psf_width > 10 || psf->psf_height > 12)
232 			{
233 				return "Hardware does not support fonts "
234 					"larger than 10x12 pixels.";
235 			}
236 		}
237 		else	/* No, colour only */
238 		{
239 			if (psf->psf_width > 8 || psf->psf_height > 8)
240 			{
241 				return "Hardware does not support fonts "
242 					"larger than 8x8 pixels.";
243 			}
244 		}
245 	}
246 	else
247 	{
248 		if (psf->psf_width > 8) return "Hardware does not support "
249 			"fonts wider than 8 pixels.";
250 		if (convertible == 1 && psf->psf_height != 8)
251 			return "Convertible only supports fonts 8 pixels high.";
252 	}
253 	return NULL;
254 }
255 
256 
herc_probe(void)257 static int herc_probe(void)
258 {
259 	unsigned n;
260 	psf_byte y;
261 	union REGS rg;
262 
263 	rg.h.ah = 0x0F;
264 /* Check that we're in mono mode */
265 	int86(0x10, &rg, &rg);
266 	if (rg.h.al != 7)
267 	{
268 		return 0;
269 	}
270 
271 	di();
272 	for (n = 0; n < 50000; n++)
273 	{
274 		y = inportb(0x3BA);
275 		if ((y & 0x80) == 0)
276 		{
277 /* OK, this is a Herc and not an MDA */
278 			ei();
279 			switch(y & 0x70)
280 			{
281 				case 0x50: return 2;	/* InColor */
282 				case 0x10: return 1;	/* Herc Plus */
283 				default:   return 0;	/* Herc */
284 			}
285 		}
286 	}
287 	ei();
288 	return 0;
289 }
290 
291 
load_set(unsigned char far * data,int count,int width,int height,int slot)292 static char *load_set(unsigned char far *data, int count,
293 		int width, int height, int slot)
294 {
295 	char *s;
296 
297 	if (apricot) s = apricot_loadfont( data, count, width, height, slot);
298 	else if (wang) s = wang_loadfont ( data, count, width, height, slot);
299 	else if (convertible) s = conv_loadfont( data, count, height, slot);
300 	else if (ega && slot < 8) s = ega_loadfont( data, count, height, slot);
301 	else if (herc) s = herc_loadfont( data, count, width, height, slot);
302 	else return "No supported video hardware present.";
303 
304 	if (s) return s;
305 	if (slot == 1)	/* Loading into slot 1. Use it for high-intensity  */
306 	{		/* characters. */
307 		union REGS rg;
308 		if (convertible)
309 		{
310 			rg.x.ax = 0x1402;
311 			rg.h.bl = 3;	/* Use slot 1 for bold */
312 			int86(0x10, &rg, &rg);
313 		}
314 		if (ega)
315 		{
316 			rg.x.ax = 0x1103;
317 			rg.h.bl = 4;	/* Use slot 1 for bold, slot 0 normal */
318 			int86(0x10, &rg, &rg);
319 		}
320 	}
321 	return NULL;
322 }
323 
324 
install_font(PSF_FILE * psf,int usealt,int unused)325 char *install_font(PSF_FILE *psf, int usealt, int unused)
326 {
327 	char *s;
328 	psf_byte far *base;
329 	long remaining;
330 	int count;
331 	int slot;
332 	int uslot;
333 
334 	base      = psf->psf_data;
335 	remaining = psf->psf_length;
336 	slot      = 0;
337 
338 	while (remaining > 0)
339 	{
340 		if (remaining < 256) count = remaining;
341 		else		     count = 256;
342 
343 		uslot = slot;
344 	/* usealt swaps the first two slots over */
345 		if (slot < 2 && usealt) uslot ^= 1;
346 	/* Load up to 256 chars into the current slot */
347 		s = load_set(base, count, psf->psf_width, psf->psf_height,
348 				uslot);
349 		if (s) return s;
350 	/* Move on to the next chunk */
351 		slot++;
352 		base += count * psf->psf_charlen;
353 		remaining -= count;
354 	}
355 	return s;
356 }
357 
358 
conv_loadfont(psf_byte far * buffer,int count,int height,int slot)359 char *conv_loadfont(psf_byte far *buffer, int count, int height, int slot)
360 {
361 	union REGS rg;
362 	struct SREGS sg;
363 
364 	/* Convertible supports only 2 fonts */
365 	if (slot >= 2) return NULL;
366 
367 	rg.x.ax = 0x1400;
368 	rg.h.bh = height;
369 	rg.h.bl = slot;
370 	rg.x.cx = count;
371 	rg.x.dx = 0;
372 	rg.x.di = FP_OFF(buffer);
373 	sg.es   = FP_SEG(buffer);
374 	int86x(0x10, &rg, &rg, &sg);
375 	return NULL;
376 }
377 
apricot_pc_loadfont(psf_byte far * buffer,int count,int width,int height,int slot)378 static char *apricot_pc_loadfont(psf_byte far *buffer, int count, int width,
379 		int height, int slot)
380 {
381 	int n, wb, x, y;
382 	unsigned char far *src;
383 	unsigned char far *dest;
384 /*	unsigned short far *ptr = MK_FP(0x70, 0x0C);
385 	unsigned char far *font = MK_FP(ptr[1], ptr[0]); */
386 	unsigned char far *font = MK_FP(0x80, 0);
387 	unsigned short w, mask, mk;
388 
389 	if (slot >= 1) return NULL;
390 	if (count > 256) count = 256;
391 	wb = (width + 7) / 8;
392 	for (n = 0; n < count; n++)
393 	{
394 		dest = font + n * 32;
395 		for (y = 0; y < 16; y++)
396 		{
397 			src  = buffer + (n * height + y) * wb;
398 			w = 0;
399 			mask = 1;
400 			mk = 0x80;
401 			if (y < height) for (x = 0; x < 10; x++)
402 			{
403 				if (x < width && ((*src & mk))) w |= mask;
404 				mask = mask << 1;
405 				mk = mk >> 1;
406 				if (mk == 0) { mk = 0x80; src++; }
407 			}
408 			dest[0] = (w & 0xFF);
409 			dest[1] &= 0xFC;
410 			dest[1] |= ((w >> 8) & 3);
411 			dest += 2;
412 		}
413 	}
414 	return NULL;
415 }
416 
apricot_f_loadfont(psf_byte far * buffer,int count,int height,int slot)417 static char *apricot_f_loadfont(psf_byte far *buffer, int count,
418 		int height, int slot)
419 {
420 	int max, n;
421 	unsigned short far *ptr = MK_FP(0x70, 0x0C);
422 	unsigned char far *font = MK_FP(ptr[1], ptr[0]);
423 
424 	if (slot >= 1) return NULL;
425 	if (height == 10) font += 0x800;
426 	if (count > 256) count = 256;
427 	max = count * height;
428 	for (n = 0; n < max; n++)
429 	{
430 		font[n] = buffer[n];
431 	}
432 	return NULL;
433 }
434 
435 
apricot_loadfont(psf_byte far * buffer,int count,int width,int height,int map)436 char *apricot_loadfont(psf_byte far *buffer, int count, int width, int height,
437 		int map)
438 {
439 	if (apricot == 1) return apricot_pc_loadfont(buffer, count, width,
440 		height, map);
441 	return apricot_f_loadfont(buffer, count, height, map);
442 }
443 
444 
wang_loadfont(psf_byte far * buffer,int count,int width,int height,int slot)445 char *wang_loadfont(psf_byte far *buffer, int count, int width, int height,
446 		int slot)
447 {
448 	switch (wang)
449 	{
450 		case 1: /* Colour only */
451 			if (slot == 0) return wang_colour_loadfont(buffer, count, width, height);
452 			return NULL;
453 		case 2: /* Mono only */
454 			if (slot == 0) return wang_mono_loadfont(buffer, count, width, height);
455 			return NULL;
456 
457 /* When CGDC is present, we need to handle these cases as well. */
458 #if 0
459 		case 4: /* CGDC only */
460 		case 5: /* CGDC and colour */
461 		case 6: /* CGDC and mono */
462 		case 7: /* All three */
463 		case 3:	/* Mono & colour */
464 #endif
465 		default:
466 			switch (slot)
467 			{
468 				case 0: /* Load into active display */
469 				if (wang_active_display_type() < 2) /* colour card? */
470 					return wang_colour_loadfont(buffer, count, width, height);
471 				else 	return wang_mono_loadfont(buffer, count, width, height);
472 				case 1:	/* Load into inactive display */
473 				if (wang_active_display_type() < 2) /* colour card? */
474 					return wang_mono_loadfont(buffer, count, width, height);
475 				else 	return wang_colour_loadfont(buffer, count, width, height);
476 			}	/* end switch(slot) */
477 	}	/* end switch(wang) */
478 	return NULL;
479 }
480 
481 /* See which display is active, and what type it is */
wang_active_display_type(void)482 static int wang_active_display_type(void)
483 {
484 	union REGS rg;
485 	struct SREGS sg;
486 	unsigned short far *ptr;
487 	unsigned char far *scr;
488 	unsigned card;
489 
490 	rg.x.ax = 1;
491 	int86x(0x88, &rg, &rg, &sg);	/* ES:BX -> BIOS config table */
492 	rg.x.bx += 8;			/* -> video cards section */
493 	ptr = MK_FP(sg.es, rg.x.bx);	/* ptr[0] = count of video cards, */
494 					/* ptr[1], [2] etc -> descriptors */
495 	for (card = 1; card <= ptr[0]; card++)
496 	{
497 		scr = MK_FP(sg.es, ptr[card]);
498 		if (scr[0] & 0x80)	/* Active display? */
499 		{
500 			return (scr[0] & 0x70) >> 4;
501 		}
502 	}
503 	return 0;
504 }
505 
506 
wang_mono_loadfont(psf_byte far * buffer,int count,int width,int height)507 static char *wang_mono_loadfont(psf_byte far *buffer, int count, int width,
508 		int height)
509 {
510 	unsigned card, slot;
511 	unsigned wb;
512 	int y0;
513 	union REGS rg;
514 	struct SREGS sg;
515 	unsigned short far *ptr;
516 	unsigned char far *scr;
517 	unsigned short bits;
518 	unsigned short far *font = MK_FP(0xF200, 0);
519 	int n, m;
520 
521 	if (count > 256) count = 256;
522 
523 	y0 = (12 - height) / 2;
524 	wb = (width + 7) / 8;
525 	if (y0 < 0) y0 = 0;
526 
527 	/* Find the BIOS data segment */
528 	rg.x.ax = 1;
529 	int86x(0x88, &rg, &rg, &sg);	/* ES:BX -> BIOS config table */
530 	rg.x.bx += 8;			/* -> video cards section */
531 	ptr = MK_FP(sg.es, rg.x.bx);	/* ptr[0] = count of video cards, */
532 					/* ptr[1], [2] etc -> descriptors */
533 	for (card = 1; card <= ptr[0]; card++)
534 	{
535 		scr = MK_FP(sg.es, ptr[card]);
536 
537 /* I do hope that having the slot number at offset 0x13 and the device class
538  * at offset 0x14 are contracts rather than implementation details. */
539 
540 		/* Is this card a class 0x11 or 0x15? */
541 		if (scr[0x14] != 0x11 && scr[0x14] != 0x15) continue;
542 
543 		/* Page the card in */
544 		slot = (scr[0x13] * 256);
545 		di();
546 		outportb(slot | 0x10, (scr[8] & 0x0C) | 1);
547 
548 		/* Load the font */
549 		for (n = 0; n < count; n++)
550 		{
551 			for (m = 0; m < y0; m++)
552 			{
553 				font[16 * n + m] = 0;
554 			}
555 			for (m = 0; m < height; m++)
556 			{
557 				if (wb == 1) bits = buffer[n * height + m];
558 				else
559 				{
560 					bits = buffer[n * height * wb + wb * m];
561 					bits = bits << 2;
562 					bits |= (buffer[n * height * wb + wb*m + 1] >> 6) & 3;
563 				}
564 				if ((m + y0) < 12)
565 				{
566 					font[16 * n + m + y0] = bits;
567 				}
568 			}
569 			for (; m < 16; m++)
570 			{
571 				font[16 * n + m + y0] = 0;
572 			}
573 		}
574 
575 		/* Page the card out */
576 		outportb(slot | 0x10, (scr[8] & 0x0C) );
577 		ei();
578 	}
579 	return NULL;
580 }
581 
wang_colour_loadfont(psf_byte far * buffer,int count,int width,int height)582 static char *wang_colour_loadfont(psf_byte far *buffer, int count, int width,
583 		int height)
584 {
585 	union REGS rg;
586 	struct SREGS sg;
587 	unsigned short far *ptr;
588 	unsigned char far *font;
589 	unsigned n, m, wb;
590 
591 	/* Find the BIOS data segment */
592 	rg.x.ax = 1;
593 	int86x(0x88, &rg, &rg, &sg);	/* ES:BX -> BIOS config table */
594 
595 	if (count > 256) count = 256;
596 	/* I have no idea if this is value is a contract or just some handy
597 	 * piece of BIOS internals. But the address of the colour screen font
598 	 * doesn't seem to be exposed anywhere else. */
599 	ptr = MK_FP(sg.es, 6);
600 	font = MK_FP(ptr[0], 0);
601 
602 	wb = (width + 7) / 8;
603 	for (n = 0; n < count; n++)
604 	{
605 		for (m = 0; m < 8; m++)
606 		{
607 			if (m < height) font[n * 8 + m] = buffer[n * wb * height + m * wb];
608 			else		font[n * 8 + m] = 0;
609 		}
610 	}
611 	return NULL;
612 }
613 
614 
615 #if HERC_CUSTOM
616 static psf_byte h_params[] =
617 {
618 	0x6D, 0x5A, 0x5C, 0x0F,	/* 8 pixels wide */
619 	0x61, 0x50, 0x52, 0x0F	/* 9 pixels wide */
620 };
621 
622 static psf_byte v_params[] =
623 {
624 	0x5C, 0x02, 0x58, 0x59,	/*  4 lines high */
625 	0x4A, 0x00, 0x46, 0x46,	/*  5 lines high */
626 	0x3D, 0x04, 0x3A, 0x3B,	/*  6 lines high */
627 	0x34, 0x06, 0x32, 0x33,	/*  7 lines high */
628 	0x2D, 0x02, 0x2B, 0x2C,	/*  8 lines high */
629 	0x28, 0x01, 0x26, 0x27,	/*  9 lines high */
630 	0x24, 0x00, 0x23, 0x23,	/* 10 lines high */
631 	0x20, 0x00, 0x1F, 0x20,	/* 11 lines high */
632 	0x1D, 0x0A, 0x1D, 0x1D,	/* 12 lines high */
633 	0x1B, 0x06, 0x1A, 0x1B,	/* 13 lines high */
634 	0x19, 0x06, 0x19, 0x19,	/* 13 lines high */
635 	0x17, 0x0A, 0x17, 0x17,	/* 13 lines high */
636 	0x16, 0x02, 0x15, 0x16,	/* 13 lines high */
637 };
638 #endif
639 
herc_loadfont(psf_byte far * buffer,int count,int width,int height,int slot)640 char *herc_loadfont(psf_byte far *buffer, int count,
641 		int width, int height, int slot)
642 {
643 	psf_byte far *dest;
644 	psf_byte far *src;
645 	psf_byte far *biosdata;
646 	unsigned n, m;
647 	int wb;
648 	static psf_byte x_ramfont = 1;
649 
650 	if (slot >= 12) return NULL;	/* Herc supports 12 fonts */
651 	if (slot > 0) x_ramfont = 5;	/* 48k RAMfont mode */
652 
653 	if (width < 8) width = 8;
654 	wb = (width + 7) / 8;
655 	biosdata = MK_FP(0x40, 0);
656 	/* Blank the screen */
657 	di();
658 	outportb(0x3B8, 0);
659 #if HERC_CUSTOM
660 	/* Reprogram the CRTC */
661 	m = (wb - 1) * 4;
662 	for (n = 0; n < 4; n++)
663 	{
664 		outportb(0x3B4, n);
665 		outportb(0x3B5, h_params[n + m]);
666 	}
667 	m = (height - 4) * 4;
668 	for (n = 0; n < 4; n++)
669 	{
670 		outportb(0x3B4, n + 4);
671 		outportb(0x3B5, v_params[n + m]);
672 	}
673 #endif
674 	/* Enable the RAM character generator */
675 	outportb(0x3B4, 0x14);	/* Select CRTC register 0x14 */
676 
677 	outportb(0x3B5, x_ramfont); /* RAMfont mode */
678 	if (slot > 4) /* Slots 5-11 require the Herc to go into Full mode */
679 	{
680 		outportb(0x3BF, 3);
681 	}
682 	else
683 	{
684 		outportb(0x3BF, 1);
685 		/* Make character RAM visible at 0xB4000 */
686 	}
687 	for (n = 0; n < count; n++)
688 	{
689 		dest = MK_FP(0xB400, slot * 4096 + n * 16);
690 		src = buffer + n * height * wb;
691 		for (m = 0; m < height; m++)
692 		{
693 			dest[m] = src[m * wb];
694 		}
695 	}
696 	outportb(0x3BF, 0x00);	/* Hide character RAM and Full-mode RAM*/
697 
698 #if HERC_CUSTOM
699 	biosdata[0x4A] = 720 / width;		/* CRT columns */
700 	biosdata[0x84] = (350 / height) - 1;	/* CRT rows minus 1 */
701 	n = (biosdata[0x84] + 1) * biosdata[0x4A]; /* CRT characters */
702 	n *= 2;
703 	biosdata[0x4C] = (n & 0xFF);
704 	biosdata[0x4D] = (n >> 8) & 0xFF;
705 #endif
706 	/* Re-enable video */
707 	outportb(0x3B8, biosdata[0x65]);
708 	ei();
709 	return NULL;
710 }
711 #undef di	/* We want to use it in asm */
712 
713 /* Resort to asm to load the font, because PPD can't pass BP through int86() */
714 #asm
715 	.globl	large_data
716 	.globl	large_code
717 	.psect	_TEXT,class=CODE
718 	.globl	_ega_loadfont
719 	.signat	_ega_loadfont,16444
720 
721 _ega_loadfont:
722 	push	bp
723 	mov	bp,sp
724 
725 	push	si
726 	push	di
727 	push	es
728 
729 	mov	dx,ax		; Count
730 	mov	di,6[bp]	; Address: offset
731 	mov	ax,8[bp]	; Address: segment
732 	mov	es,ax		; ES:DI->font
733 	mov	cx,10[bp]	; Height
734 	mov	bx,12[bp]	; Font to load
735 	mov	bh,cl		; BH=height BL=map
736 
737 	mov	ax,#0x1110	; Function
738 	mov	cx,dx		; Count
739 	mov	dx,#0		; First char
740 	push	bp
741 	mov	bp,di		; ES:BP->font
742 	int	#0x10
743 	pop	bp
744 	pop	es
745 	pop	di
746 	pop	si
747 	pop	bp
748 	mov	dx,#0
749 	mov	ax,#0		; Return NULL
750 	retf	#8
751 #endasm
752 
753 
754