ÿ\NLETTER\6.6\TRIANGLE.C/* Copyright (C) Magna Carta Software, 1988. All Rights Reserved. Scroll and pan a graphics image on the screen in video mode 0X10 (640x350 16-color graphics). Note: This program assumes the presence of an EGA driving an Enhanced Color Display or a VGA. Furthermore, that this adapter is active. Simple invocataion syntax: TURBO C: tcc sgraph MSC v5.0+: cl /DMSC sgraph.c POWER C v1.1+ pc /e sgraph WATCOM C v6.0+ wcc sgraph -ms wlink FILE sgraph LIB ?:\watcomc\lib\clibs, ?:\watcomc\lib\maths OPTION Map, caseexact, stack=2048 First version: 5/7/88. Last revised: 6/1/88. */ #include #include #include typedef unsigned char BYTE; /* COMPILER SPECIFIC DETAILS */ #if defined(MSC) || defined(__WATCOMC__) #define inportb(port) inp(port) #define outportb(port,value) outp((port),(value)) #define outport(port,value) outpw((port),(value)) #endif /* MANIFEST CONSTANTS */ #define FALSE 0 #define TRUE !FALSE #define LOGICAL_WIDTH 132 /* logical screen width in bytes */ #define VIDEO 0X10 /* BIOS interrupt 0X10 -- video */ #define YELLOW 14 /* the color for the graphics image */ /* EGA and VGA register values */ #define START_ADDRESS_HIGH 0X0C /* Address of start address h reg. of CRTC */ #define START_ADDRESS_LOW 0X0D /* Address of start address l reg. of CRTC */ #define AC_INDEX 0X3C0 /* Attribute Controller Index Register */ #define AC_HPP 0X13 | 0X20 /* Horizontal Pel Panning Register */ /* Note: ORed with 20H to preserve */ /* bit 5 */ #define OFFSET_REG 0X013 /* GLOBAL VARIABLES */ unsigned right_edge, left_edge; /* TRUE if we are at the edge of the screen */ int vpel, hpel; int mono = FALSE; /* EGA monochrome */ struct video_descriptor { BYTE far *base; /* starting address of the video buffer */ unsigned isr; /* EGA/VGA input status register address */ unsigned crtc; /* CRT Controller Register address */ }; struct screen_descriptor { unsigned rows; unsigned cols; unsigned logical_width; /* screen logical width in words. max: 0X100 */ unsigned start_addr; /* screen start address. max: 0X4000 */ }; struct video_descriptor video; struct screen_descriptor screen; /* FUNCTION PROTOTYPES */ int fill_triag(int color, int v[][2]); void scanline(unsigned x0, unsigned x1, unsigned y, int color); unsigned smooth_scroll(int count, unsigned speed); unsigned smooth_pan(int count, unsigned speed); unsigned set_logical_screen_width(unsigned l_width); void set_start_addr(unsigned start_addr); void set_pel_pan(int hpel); void main(void) { union REGS regs; /* top ----- left corner - right corner */ static int v1[][2] = {{100, 25}, {50, 125}, {150, 125}}; /* ESTABLISH THE SYSTEM STATUS */ /* initialize the base address of the video buffer */ video.base = (BYTE far *) 0XA0000000L; screen.start_addr = 0; /* initialize the start address */ regs.x.ax = 0X0010; /* set video mode to 10 hex. */ int86(0X10, ®s, ®s); /* initialize the logical screen width in bytes */ screen.logical_width = LOGICAL_WIDTH; screen.cols = screen.logical_width; video.isr = 0X03da; /* address of input status register */ video.crtc = video.isr - 6; /* address of CRT controller register */ /* SET THE LOGICAL SCREEN WIDTH FOR PANNING AND THE START ADDRESS */ set_logical_screen_width(screen.logical_width); set_start_addr(screen.start_addr); /* WE MUST DO A DUMMY READ OF THE INPUT STATUS REGISTER TO INITIALIZE THE TOGGLE OF THE ATTRIBUTE CONTROLLER (see text of article ) */ inportb(video.isr); fill_triag(YELLOW, v1); /* draw the graphic image on the screen */ /* ALL THE SCROLLING-PANNING ACTION IS HANDLED HERE */ do { smooth_scroll(-100,2); smooth_pan(-300,1); smooth_scroll(100,2); smooth_pan(300,1); } while (!kbhit()); /* A KEY WAS HIT -- RETURN TO 80 COLUMN COLOR TEXT MODE AND EXIT */ getch(); regs.x.ax = 0X0003; int86(0X10, ®s, ®s); } /* Fill an isosceles triangle with one side contiguous with a scan line and an apex at the top. */ int fill_triag(int color, int v[][2]) { int y, start, end; double left_slope, right_slope, x0, x1, x2, y0, y1, y2; x0 = v[0][0]; x1 = v[1][0]; x2 = v[2][0]; y0 = v[0][1]; y1 = v[1][1]; y2 = v[2][1]; left_slope = (x1 - x0)/(y1 - y0); /* Compute slopes */ right_slope = (x2 - x0)/(y2 - y0); /* Compute slopes */ for (y = y0; y <= y1; y++) { /* Loop over raster lines */ start = x0 + (y - y0)*left_slope; end = x0 + (y - y0)*right_slope; scanline(start,end,y,color); /* Fill next section */ } return (0); } void scanline(unsigned start, unsigned stop, unsigned raster, int color) { #define GRAPHICSC 0X03CE #define SEQUENCER 0X03C4 #define BIT_MASK 8 unsigned i, mask; BYTE oddbits, byte, far *s_offset; int scan_len; scan_len = stop - start; /* length -1 in pixels */ /* --- Load SET/RESET registers with current color */ outport(GRAPHICSC, color << 8); /* move color into reset register */ outport(GRAPHICSC, 0X0F01); /* enable use of reset register */ outport(SEQUENCER, 0X0F02); /* enable all planes for write */ /* ----------- COMPUTE ADDRESS AND MASK FOR FIRST PIXEL ------------- */ s_offset = video.base + screen.logical_width*raster + (start >> 3); if ( (oddbits = start & 7) != 0) { mask = 0X00FF >> oddbits; scan_len += oddbits; scan_len -= 8; if (scan_len < 0) { scan_len = (~scan_len) + 1; mask = mask >> (BYTE) scan_len; mask = mask << (BYTE) scan_len; scan_len = 0; } outport(GRAPHICSC, (mask << 8) | BIT_MASK); /* unmask bit mask */ byte = *s_offset; /* latch data */ *s_offset++ = byte; /* write data */ } /* ---------------- WRITE COMPLETE BYTES -------------------- */ if (scan_len >= 8) { outport(GRAPHICSC, (0XFFFF << 8) | BIT_MASK); /* unmask bit mask */ for (i=0; i < (scan_len >> 3); i++) *s_offset++ = byte; } /* --- COMPUTE ADDRESS AND MASK FOR LAST PIXEL ------------- */ if ( (oddbits = scan_len & 7) != 0) { mask = (0XFF >> oddbits) ^ 0XFF; outport(GRAPHICSC, (mask << 8) | BIT_MASK); byte = *s_offset; /* latch data */ *s_offset++ = byte; /* write data */ } /* RESTORE BIT MASK AND SET/RESET REGISTERS */ outport(GRAPHICSC, (0XFFFF << 8) | BIT_MASK); outport(GRAPHICSC, 0X0001); } /* SMOOTH_SCROLL scrolls the EGA video buffer the number of scan lines indicated in "count" at a speed of "speed" scan lines per vertical retrace. */ unsigned smooth_scroll(int count, unsigned speed) { unsigned i, max_count; max_count = (count < 0) ? -count : count; /* GET THE START ADDRESS OF THE SCREEN BUFFER */ outportb(video.crtc, START_ADDRESS_HIGH); /* High byte */ screen.start_addr = inportb(video.crtc+1) << 8; outportb(video.crtc, START_ADDRESS_LOW); /* Low byte */ screen.start_addr |= inportb(video.crtc+1); /* COUNT > 0 => SCROLL SCREEN IMAGE UPWARDS. */ for(i=0;i < max_count; i+=speed) { screen.start_addr = (count > 0) ? screen.start_addr + screen.logical_width*speed : screen.start_addr - screen.logical_width*speed; /* wait for the end of a vertical retrace */ while (inportb(video.isr) & 8); /* wait for the next vertical retrace */ while (!(inportb(video.isr) & 8)); set_start_addr(screen.start_addr); } return (screen.start_addr); } /* SMOOTH_PAN This function invokes smooth panning on the EGA/VGA. The function calculates the number of scan lines per row. The speed variable adjusts the speed in pixels per vertical retrace and takes values in the range 1-8. */ unsigned smooth_pan(int count, unsigned speed) { unsigned i; /* count greater than zero (move viewport to the right) */ if (count>0 && !right_edge) for(i=0;i=8) { screen.start_addr++; left_edge = FALSE; if (!(screen.start_addr % (screen.logical_width))) right_edge = TRUE; if (!right_edge) hpel %= 8; /* reset the pel counter */ else { hpel = 0; i = count - 1; } set_start_addr(screen.start_addr); } for(;hpel < 8 && i < count;i+=speed, hpel+=speed) set_pel_pan(hpel); } /* count less than zero (move viewport to the left) */ else if (count<0) { if (hpel>7) hpel = hpel - 8; for(i=0;i<(-count) ;) { /* if we have scrolled a full character, reset start address */ if (hpel<0 && !left_edge) { screen.start_addr--; right_edge = FALSE; if (!(screen.start_addr % (screen.logical_width))) left_edge = TRUE; hpel = 8 + hpel; set_start_addr(screen.start_addr); } else if (hpel<0 && left_edge) i = (-count); for(;hpel >= 0 && i < (-count);i+=speed, hpel-=speed) set_pel_pan(hpel); } } return (screen.start_addr); } /* SET_LOGICAL_SCREEN_WIDTH defines an EGA/VGA screen width for smooth panning and sets the the global variable "screen.cols." screen.cols must be restored when smooth panning is finished or subsequent screen writes will address the wrong screen locations. l_width is the width of the logical screen in bytes. Max 512. */ unsigned set_logical_screen_width(unsigned l_width) { l_width = (l_width > 512) ? 512 : l_width; /* set screen_cols */ screen.cols = l_width; /* set logical screen width */ l_width >>= 1; /* convert to words */ outport(video.crtc, (l_width << 8) | OFFSET_REG); return (l_width << 1); } /* SET_START_ADDRESS -- Sets the start address of the screen. I.e. the */ /* address that occupies the top left of the screen. */ void set_start_addr(unsigned start_addr) { /* address the start address high register */ outport(video.crtc, (start_addr & 0XFF00) | START_ADDRESS_HIGH); /* address the start address low register */ outport(video.crtc, (start_addr << 8) | START_ADDRESS_LOW); } /* SET_PEL_PAN -- Sets the horizontal pel panning register to the value */ /* specified in "hpel". Called by smooth_pan() */ void set_pel_pan(int hpel) { /* first wait for end of vertical retrace */ while (inportb(video.isr) & 8); /* wait for next vertical retrace */ while (!(inportb(video.isr) & 8)); /* address the horizontal pel paning register */ outportb(AC_INDEX, AC_HPP); outportb(AC_INDEX, hpel); }