/*LINTLIBRARY*/ #ifndef lint static char sccsid[] = "@(#)ibmpc.c 1.4 89/12/12" ; #endif /* Popi device driver for a PC using one of: * Borland Turbo C * Microsoft C * MIX Power C * Written by Stephen Frede, Softway Pty Ltd. * * Popi was originally written by Gerard J. Holzmann - AT&T Bell Labs. * This version is based on the code in his Prentice Hall book, * "Beyond Photography - the digital darkroom," ISBN 0-13-074410-7, * which is copyright (c) 1988 by Bell Telephone Laboratories, Inc. * * Permission is given to distribute these extensions, as long as these * introductory messages are not removed, and no monies are exchanged. * * No responsibility is taken for any errors or inaccuracies inherent * either to the comments or the code of this program, but if reported * (see README file) then an attempt will be made to fix them. */ /* * There is still some work to be done here: * + vid_detect() needs to be written. * + HGC code needs to be fixed properly. * + other standard modes need to be added. * + Dithering or something needs to be added for EGA (sim to CGA). * + Handle image allocation properly with far pointers * where appropriate (ie unavoidable). */ #if defined(__STDC__) && defined(__TURBOC__) /* The user has (with commendable intentions) used the Turbo C * ANSI compatibility flag. Unfortunately, we need the nonstandard * extensions to include and use far pointers. */ #include "Unfortunately, can't use ANSI compatibility flag." #endif /* __STDC__ && __TURBOC__ */ #include "popi.h" /* I don't know what token Power C defines to distinguish itself. * But it does define a max() macro in stdlib.h include file. * Hopefully no other vendor is silly enough to do this, so we * can use that to distinguish Power C. */ #if defined(__STDC__) && defined(max) #define __POWERC__ 1 #endif /* __STDC__ && max */ /* We assume that the compiler has these files header files. */ #include #include /* We only use the __TURBOC__ token to test for graphics related * features from here on, so if we have an old version with no * graphics support, just pretend we are a generic compiler. */ #if defined(__TURBOC__) # if __TURBOC__ < 0x0200 # undef __TURBOC__ # endif /* __TURBOC__ < 0x0200 */ #endif /* __TURBOC__ */ #if __TURBOC__ || __POWERC__ # include #endif /* __TURBOC__ || __POWERC__ */ #if __MSC__ #include #endif /* __MSC__ */ #if defined(__TURBOC__) # ifndef BGIPATH # define STRBGIPATH "" # else # define STR(x) #x # define STRBGIPATH STR(BGIPATH) # endif /* BGIPATH */ #endif /* __TURBOC__ */ #ifndef MK_FP #define MK_FP(segment,offset) ((void far *) \ (((unsigned long)(segment) << 16) | (unsigned)(offset))) #endif /* MK_FP */ /* There are ten exportable routines used by the popi program. * * These are: * * disp_init(argc, argv) - called from main at the start. * disp_finish() - called from main prior to exit. * disp_imgstart() - called prior to drawing an image. * disp_imgend() - called after drawing an image. * disp_putline(line, y) - to draw an image scanline. * disp_getchar() - to get the next character typed. * disp_ungetc(c) - put back the last character typed. * disp_prompt() - display popi prompt and clear input buffer. * disp_error(errtype,pos) - display error message. * disp_percentdone(n) - display percentage value of conversion. */ /* * Bios function call definitions */ #define BIOS_VIDFN 0x10 /* Video functions */ #define VID_PALETTE 0x10 /* palette access function */ #define PAL_SETPAL 0x00 /* update single palette register */ #define PAL_SETPALBLOCK 0x02 /* update all palette registers */ #define PAL_GETPALBLOCK 0x09 /* read all palette registers */ #define PAL_GETDACBLOCK 0x17 /* read current VGA DAC values */ #define PAL_SETDACBLOCK 0x12 /* set new VGA DAC values */ /* * Arguments to the vid_vgapalette and vid_setpalette functions */ #define PAL_SAVE 0 /* Save existing values */ #define PAL_RESTORE 1 /* Restore saved values */ #define PAL_SETGREY 2 /* Set greyscale mapping */ #define PAL_SETLIN 3 /* Set 1:1 mapping (vid_setpalette) */ /* Bios Addresses */ #define SEG_VIDBIOS 0x0040 /* Segment of video BIOS addresses */ #define BIOSADDR_MODE 0x0049 /* Current BIOS video mode number */ #define BIOSADDR_CRTCPORT 0x0063 /* Port Address of CRTC */ /* Fixed port addresses */ #define HGC_SWITCH 0x03BF /* Prototypes for local functions */ void vid_vgapalette P((int)); void vid_setpalette P((int)); void vid_setmode P((unsigned char)); void vid_sethgc P((void)); void vid_setpixel P((int, int, pixel_t)); unsigned char vid_detect P((void)); /* Array containing threshold values for dithering. */ pixel_t thresh[BITSPERPIXEL][BITSPERPIXEL] = { { 0, 128, 32, 160, 8, 136, 40, 168, }, { 192, 64, 224, 96, 200, 72, 232, 104, }, { 48, 176, 16, 144, 56, 184, 24, 152, }, { 240, 112, 208, 80, 248, 120, 216, 88, }, { 12, 140, 44, 172, 4, 132, 36, 164, }, { 204, 76, 236, 108, 196, 68, 228, 100, }, { 60, 188, 28, 156, 52, 180, 20, 148, }, { 252, 124, 220, 92, 244, 116, 212, 84, }, }; struct VidDriver { unsigned char bios_mode; /* Bios mode no. */ unsigned int xsize, /* Pixels/scanline */ ysize; /* Scanlines/image */ /* Segment to which screen buffer is mapped */ unsigned int ScreenMem; unsigned int interlace, /* no. lines interlaced */ interlace_offset; /* memory offset for interlace */ unsigned char PixelsPerByte; }; static struct VidDriver VidDrivers[] = { #define VID_VGA 0 { 0x13, 320, 200, 0xA000, 1, 0, 1, }, #define VID_EGA 1 { 0x10, 640, 350, 0xA000, 2, 0x2000, 8, }, #define VID_CGA 2 { 0x06, 640, 200, 0xB800, 2, 0x2000, 8, }, #define VID_STD 2 /* Last standard adapter */ /* From here on, adapters have no bios support. The bios * number will be stored in the appropriate place in the video * bios data area (this is useful eg if a mouse is being used, * so it gets the graphics cursor correct). */ #define VID_HGC 3 { 0x06, 720, 348, 0xB000, 4, 0x2000, 8, }, }; #define VID_NONE -1 /* Unknown video adapter */ /* When VidAdapter is VID_LIB, use compile-time dependant * library routines. For example, the Turbo C graphics library * allows custom drivers to be added for nonstandard adapters. */ #define VID_LIB -2 static struct VidDriver *VidDriver = 0; static int VidAdapter = VID_NONE, vid_xbytes, vid_xoff_bytes, vid_yoff; #if __TURBOC__ static int tc_driver, tc_mode, tc_error; #endif /* __TURBOC__ */ static unsigned char far *ScreenMem; /* Pointer to current mode in Bios video display data area */ static char far *VidModeBiosAddr = MK_FP(SEG_VIDBIOS, BIOSADDR_MODE); /* Pointer to CRTC register port number in Bios dideo display area */ static short far *CrtcPortBiosAddr = MK_FP(SEG_VIDBIOS, BIOSADDR_CRTCPORT); static char VidInitMode; /* * Routine to map VGA palette registers (256 colour mode) * to and from a greyscale display. */ #define BITSPERBYTE 8 #define BITSPERVGACOLOUR 6 /* no. VGA DAC registers */ #define SIZE_DACS 256 /* no. distinct greyscale values */ #define NUM_GREY (1 << BITSPERVGACOLOUR) /* no. palette registers that map * to the same greyscale value */ #define GREY_SAME (SIZE_DACS / NUM_GREY) /* no. underlying colours on vga */ #define PRIMARY_COLOURS 3 static void vid_vgapalette(action) int action; { union REGS regs; struct SREGS sreg; static unsigned char SaveDacs[SIZE_DACS * PRIMARY_COLOURS], *GreyDacs = 0; switch(action) { case PAL_SAVE: /* Save existing palette */ regs.h.ah = VID_PALETTE; regs.h.al = PAL_GETPALBLOCK; regs.x.bx = 0; /* first palette register */ regs.x.cx = SIZE_DACS; /* read all palette registers */ segread(&sreg); sreg.es = sreg.ds; regs.x.dx = (unsigned int) SaveDacs; int86x(BIOS_VIDFN, ®s, ®s, &sreg); break; case PAL_RESTORE: /* restore saved palette */ regs.h.ah = VID_PALETTE; regs.h.al = PAL_SETDACBLOCK; regs.x.bx = 0; /* first palette register */ regs.x.cx = SIZE_DACS; /* read all palette registers */ segread(&sreg); sreg.es = sreg.ds; regs.x.dx = (unsigned int) SaveDacs; int86x(BIOS_VIDFN, ®s, ®s, &sreg); break; case PAL_SETGREY: /* set palette to greyscale */ if (GreyDacs == 0) { unsigned char *p, c; if ((GreyDacs = malloc(SIZE_DACS*PRIMARY_COLOURS)) == 0) { sprintf(ErrBuf, "Dacs allocation failed"); error(ERR_SYS); return; } for (p = GreyDacs, c = 0; c < NUM_GREY; ++c) { int i; for (i = 0; i < PRIMARY_COLOURS * GREY_SAME; ++i) *p++ = c; } } regs.h.ah = VID_PALETTE; regs.h.al = PAL_SETDACBLOCK; regs.x.bx = 0; /* first palette register */ regs.x.cx = SIZE_DACS; /* read all palette registers */ segread(&sreg); sreg.es = sreg.ds; regs.x.dx = (unsigned int) GreyDacs; int86x(BIOS_VIDFN, ®s, ®s, &sreg); break; } } /* * Routine to map 16 palette registers. */ #define SIZE_PALETTE 17 /* 16 + overscan */ static void vid_setpalette(action) int action; { union REGS regs; struct SREGS sreg; static unsigned char SavePalette[SIZE_PALETTE], *GreyPalette = 0; unsigned char palette; switch (action) { case PAL_SAVE: /* Save existing palette */ regs.h.ah = VID_PALETTE; regs.h.al = PAL_GETPALBLOCK; segread(&sreg); sreg.es = sreg.ds; regs.x.dx = (unsigned int) SavePalette; int86x(BIOS_VIDFN, ®s, ®s, &sreg); break; case PAL_RESTORE: /* restore saved palette */ regs.h.ah = VID_PALETTE; regs.h.al = PAL_SETPALBLOCK; segread(&sreg); sreg.es = sreg.ds; regs.x.dx = (unsigned int) SavePalette; int86x(BIOS_VIDFN, ®s, ®s, &sreg); break; case PAL_SETGREY: /* set palette to greyscale */ if (GreyPalette == 0) { if ((GreyPalette = (unsigned char *) malloc(SIZE_PALETTE)) == 0) { sprintf(ErrBuf, "Palette allocation failed"); error(ERR_SYS); return; } GreyPalette[0] = 000; GreyPalette[1] = 070; GreyPalette[2] = 007; GreyPalette[3] = 077; GreyPalette[SIZE_PALETTE-1] = SavePalette[SIZE_PALETTE-1]; } regs.h.ah = VID_PALETTE; regs.h.al = PAL_SETPALBLOCK; segread(&sreg); sreg.es = sreg.ds; regs.x.dx = (unsigned int) GreyPalette; int86x(BIOS_VIDFN, ®s, ®s, &sreg); break; case PAL_SETLIN: for (palette = 0; palette < SIZE_PALETTE; ++palette) { union REGS regs; regs.h.ah = VID_PALETTE; regs.h.al = PAL_SETPAL; regs.h.bh = palette; regs.h.bl = palette; int86(BIOS_VIDFN, ®s, ®s); } break; } } static void vid_setmode(mode) unsigned char mode; { union REGS regs; regs.h.ah = 0; regs.h.al = mode; int86(0x10, ®s, ®s); } static void vid_setpixel(x, y, value) int x, y; pixel_t value; { union REGS regs; regs.h.al = value; regs.h.ah = 0x0C; regs.h.bh = 0; regs.x.cx = (unsigned) x; regs.x.dx = (unsigned) y; int86(0x10, ®s, ®s); } /* * Detect installed graphics hardware. */ static unsigned char vid_detect() { /* Yes, well, when I get a chance I'll do this. * Wait for the next release. */ return VID_NONE; } /*ARGSUSED*/ void disp_init(argc,argv) /* called from main at the start. */ int argc; char *argv[]; { /* Some compilers (eg Power C) don't get the initialisation right, * so repeat it here. */ VidModeBiosAddr = MK_FP(SEG_VIDBIOS, BIOSADDR_MODE); CrtcPortBiosAddr = MK_FP(SEG_VIDBIOS, BIOSADDR_CRTCPORT); VidInitMode = *VidModeBiosAddr; VidAdapter = vid_detect(); #if __TURBOC__ tc_driver = DETECT; detectgraph(&tc_driver, &tc_mode); switch (tc_driver) { case VGA: VidAdapter = VID_VGA; break; case EGA: case EGA64: VidAdapter = VID_EGA; break; case CGA: VidAdapter = VID_CGA; break; default: initgraph(&tc_driver, &tc_mode, STRBGIPATH); tc_error = graphresult(); if (tc_error < 0) { sprintf(ErrBuf, "initgraph error (%s)", grapherrormsg(tc_error)); error(ERR_WARN); VidAdapter = VID_NONE; return; } else VidAdapter = VID_LIB; } #endif /* __TURBOC__ */ #if __MSC__ if (_setvideomode(_VRES2COLOR)) VidAdapter = VID_VGA; else if (_setvideomode(_ERESNOCOLOR)) VidAdapter = VID_EGA; else if (_setvideomode(_HRESBW)) ; else VidAdapter = VID_NONE; #endif /* __MSC__ */ #if __POWERC__ if (setvmode(17) == 17) VidAdapter = VID_VGA; else if (setvmode(15) == 15) VidAdapter = VID_EGA; else if (setvmode(6) == 6) VidAdapter = VID_CGA; else if (setvmode(99) == 99) VidAdapter = VID_HGC; #endif /* __POWERC__ */ /* This is also a last minute addition that needs to * be cleaned up - again, next release. */ for (++argv; *argv; ++argv) { char *card; if (**argv == '-') switch ((*argv)[1]) { case 'c': /* card */ card = *argv + 2; if (strcmp(card, "vga") == 0) VidAdapter = VID_VGA; else if (strcmp(card, "ega") == 0) VidAdapter = VID_EGA; else if (strcmp(card, "cga") == 0) VidAdapter = VID_CGA; else if (strcmp(card, "hgc") == 0) VidAdapter = VID_HGC; else if (strcmp(card, "lib") == 0) VidAdapter = VID_LIB; } } if (VidAdapter == VID_NONE) { vid_setmode(VidInitMode); sprintf(ErrBuf, "No graphics hardware detected"); error(ERR_WARN); return; } switch (VidAdapter) { case VID_LIB: return; case VID_VGA: vid_setpalette(PAL_SAVE); vid_setpalette(PAL_SETLIN);/**/ vid_vgapalette(PAL_SAVE); break; case VID_EGA: vid_setpalette(PAL_SAVE); break; case VID_CGA: break; } vid_setmode(VidInitMode); VidDriver = &VidDrivers[VidAdapter]; if (Xsize > VidDriver->xsize) Xsize = VidDriver->xsize; if (Ysize > VidDriver->ysize) Ysize = VidDriver->ysize; vid_xbytes = VidDriver->xsize / VidDriver->PixelsPerByte; vid_yoff = (VidDriver->ysize - Ysize) / 2; vid_xoff_bytes = ((VidDriver->xsize - Xsize) / 2) / VidDriver->PixelsPerByte; ScreenMem = MK_FP(VidDriver->ScreenMem, 0); } void disp_finish() /* called from main prior to exit. */ { #if __TURBOC__ if (VidAdapter == VID_LIB) closegraph(); #endif /* __TURBOC__ */ } /* * Set up Hercules Graphics Card 720 * 348 graphics mode */ static void vid_sethgc() { unsigned short ModeControl = 0x03b8; unsigned short CrtcAddress = 0x03b4; unsigned short CrtcData = CrtcAddress + 1; outportb(HGC_SWITCH, 0x01); /* allow graphics mode */ outportb(ModeControl, 0x00); /* Video disabled during setup */ /* Horizontal total: 54 "characters" (at 16 pixels/char) */ outportb(CrtcAddress, 0x00); outportb(CrtcData, 0x35); /* Horizontal displayed: 45 "characters" */ outportb(CrtcAddress, 0x01); outportb(CrtcData, 0x2d); /* Horizontal sync position: at 45th character */ outportb(CrtcAddress, 0x02); outportb(CrtcData, 0x2e); /* Horizontal sync width: 7 character clocks */ outportb(CrtcAddress, 0x03); outportb(CrtcData, 0x07); /* Vertical total: 94 "characters" (at 4 scanlines/char) */ outportb(CrtcAddress, 0x04); outportb(CrtcData, 0x5b); /* Vertical adjust: 2 scanlines */ outportb(CrtcAddress, 0x05); outportb(CrtcData, 0x02); /* Vertical displayed: 87 "character" rows */ outportb(CrtcAddress, 0x06); outportb(CrtcData, 0x57); /* Vertical sync position: after 87th char row */ outportb(CrtcAddress, 0x07); outportb(CrtcData, 0x57); /* Max scan line: 4 lines/char */ outportb(CrtcAddress, 0x09); outportb(CrtcData, 0x03); *VidModeBiosAddr = 6; /* Microsoft mouse: HGC using CRT page 0 */ outportb(ModeControl, 0x0a); /* Video enabled, Graphics mode */ } void disp_imgstart() /* called prior to drawing an image. */ { if (VidAdapter == VID_NONE) return; if (VidAdapter == VID_LIB) { #if __TURBOC__ setgraphmode(tc_mode); #endif /* __TURBOC__ */ return; } if (VidAdapter <= VID_STD) vid_setmode(VidDriver->bios_mode); else *VidModeBiosAddr = VidDriver->bios_mode; /* fake it */ switch (VidAdapter) { case VID_VGA: vid_vgapalette(PAL_SETGREY); break; case VID_EGA: vid_setpalette(PAL_SETGREY); break; case VID_HGC: vid_sethgc(); break; } } void disp_imgend() /* called after drawing an image. */ { (void) getch(); switch (VidAdapter) { case VID_VGA: vid_vgapalette(PAL_RESTORE); vid_setpalette(PAL_RESTORE); break; case VID_EGA: vid_setpalette(PAL_RESTORE); break; } vid_setmode(VidInitMode); } void disp_putline(line, y) /* called to draw image scanline y. */ pixel_t *line; int y; { register int x; register unsigned char far *p; if (VidAdapter == VID_NONE) return; if (VidAdapter == VID_LIB) { for (x = 0; x < Xsize; ++x) { pixel_t val; val = *line++ < thresh[y % BITSPERPIXEL][x % BITSPERPIXEL] ? (pixel_t) 0 : (pixel_t) 1; #if __TURBOC__ putpixel(x, y, val); #endif /* __TURBOC__ */ #if __MSC__ _setcolor(val); _setpixel(x, y); #endif /* __MSC__ */ #if __POWERC__ pen_color(val); setpixel(x, y); #endif /* __POWERC__ */ } return; } p = &ScreenMem[ ( ((y + vid_yoff) % VidDriver->interlace) * VidDriver->interlace_offset ) + (y + vid_yoff) / VidDriver->interlace * vid_xbytes /* + vid_xoff_bytes */ ]; switch (VidAdapter) { case VID_VGA: for (x = 0; x < Xsize; ++x) *p++ = *line++; break; case VID_EGA: /* 4 grey levels isn't enough - need to do dithering as well, * or perhaps fake it with some colours. */ for (x = 0; x < Xsize; ++x) vid_setpixel(x, y, *line++ / 64); break; case VID_CGA: for (x = 0; x < Xsize;) { register unsigned char t; register unsigned char val; register unsigned char byte; register int pixel; byte = 0; for (pixel = 8; pixel > 0; ++x) { t = thresh[y % BITSPERPIXEL][x % BITSPERPIXEL]; val = *line++; byte |= (val > t * 2 / 3 ? 1 : 0) << --pixel; byte |= (val > (t * 2 + Zsize) / 3 ? 1 : 0) << --pixel; } *p++ = byte; } break; case VID_HGC: for (x = 0; x < Xsize;) { register char t; register unsigned char byte; register int pixel; byte = 0; for (pixel = 8; pixel > 0; ++x) { t = *line++ < thresh[y % BITSPERPIXEL][x % BITSPERPIXEL] ? (pixel_t) 0 : (pixel_t) 1; byte |= t << --pixel; } *p++ = byte; } break; } } int disp_getchar() /* get next user typed character. */ { return(getchar()); } /*ARGSUSED*/ void disp_ungetc(c) /* put back the last character typed. */ int c; { UNGETC(c, stdin); } int disp_prompt() /* display popi prompt. */ { char *prompt = "-> "; PRINTF(prompt); return sizeof prompt - 1; } void disp_error(errtype, pos) /* display error message. */ int errtype, pos; { extern int errno; extern char *sys_errlist[]; if (errtype & ERR_PARSE) { int i; for (i=1; i < pos; ++i) PUTC('-', stderr); PUTC('^', stderr); PUTC('\n', stderr); } FPRINTF(stderr, "%s\n", ErrBuf); /* we assume errno hasn't been reset by the preceding output */ if (errtype & ERR_SYS) FPRINTF(stderr, "\t(%s)\n", sys_errlist[errno]); } void disp_percentdone(percent) int percent; { static int lastpercent = 100; if (!Verbose) return; if (percent == 100) { printf("\r \n"); return; } if (percent != lastpercent) { printf("\r%2d%% ", percent); fflush(stdout); lastpercent = percent; } }