home *** CD-ROM | disk | FTP | other *** search
/ Black Art of 3D Game Programming / Black_Art_of_3D_Game_Programming.iso / source / msc / chap_3 / black3.c next >
Text File  |  1994-10-13  |  15KB  |  537 lines

  1.  
  2. // I N C L U D E S ///////////////////////////////////////////////////////////
  3.  
  4. #include <io.h>
  5. #include <conio.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <dos.h>
  9. #include <bios.h>
  10. #include <fcntl.h>
  11. #include <memory.h>
  12. #include <malloc.h>
  13. #include <math.h>
  14. #include <string.h>
  15.  
  16. #include "black3.h"   // the header file for this module
  17.  
  18. // G L O B A L S //////////////////////////////////////////////////////////////
  19.  
  20. unsigned char far *video_buffer = (unsigned char far *)0xA0000000L;
  21. unsigned char far *rom_char_set = (unsigned char far *)0xF000FA6EL;
  22.  
  23. // F U N C T I O N S /////////////////////////////////////////////////////////
  24.  
  25. void Print_Char(int xc,int yc,char c,int color,int transparent)
  26. {
  27. // this function is used to print a character on the screen. It uses the
  28. // internal 8x8 character set to do this. Note that each character is
  29. // 8 bytes where each byte represents the 8 pixels that make up the row
  30. // of pixels
  31.  
  32. int offset,               // offset into video memory
  33.          x,               // loop variable
  34.          y;               // loop variable
  35.  
  36. unsigned char far *work_char; // pointer to character being printed
  37.  
  38. unsigned char bit_mask;       // bitmask used to extract proper bit
  39.  
  40. // compute starting offset in rom character lookup table
  41. // multiple the character by 8 and add the result to the starting address
  42. // of the ROM character set
  43.  
  44. work_char = rom_char_set + c * ROM_CHAR_HEIGHT;
  45.  
  46. // compute offset of character in video buffer, use shifting to multiply
  47.  
  48. offset = (yc << 8) + (yc << 6) + xc;
  49.  
  50. // draw the character row by row
  51.  
  52. for (y=0; y<ROM_CHAR_HEIGHT; y++)
  53.     {
  54.     // reset bit mask
  55.  
  56.     bit_mask = 0x80;
  57.  
  58.     // draw each pixel of this row
  59.  
  60.     for (x=0; x<ROM_CHAR_WIDTH; x++)
  61.         {
  62.         // test for transparent pixel i.e. 0, if not transparent then draw
  63.  
  64.         if ((*work_char & bit_mask))
  65.              video_buffer[offset+x] = (unsigned char)color;
  66.  
  67.         else
  68.         if (!transparent)               // takes care of transparency
  69.             video_buffer[offset+x] = 0; // make black part opaque
  70.  
  71.         // shift bit mask
  72.  
  73.         bit_mask = (bit_mask>>1);
  74.  
  75.         } // end for x
  76.  
  77.     // move to next line in video buffer and in rom character data area
  78.  
  79.     offset      += MODE13_WIDTH;
  80.     work_char++;
  81.  
  82.     } // end for y
  83.  
  84. } // end Print_Char
  85.  
  86. //////////////////////////////////////////////////////////////////////////////
  87.  
  88. void Print_String(int x,int y,int color, char *string,int transparent)
  89. {
  90. // this function prints an entire string on the screen with fixed spacing
  91. // between each character by calling the Print_Char() function
  92.  
  93.  int index,   // loop index
  94.      length;  // length of string
  95.  
  96. // compute length of string
  97.  
  98. length = strlen(string);
  99.  
  100. // print the string a character at a time
  101.  
  102. for (index=0; index<length; index++)
  103.      Print_Char(x+(index<<3),y,string[index],color,transparent);
  104.  
  105. } // end Print_String
  106.  
  107. ///////////////////////////////////////////////////////////////////////////////
  108.  
  109. void Write_Pixel(int x,int y,int color)
  110. {
  111.  
  112. // plots the pixel in the desired color a little quicker using binary shifting
  113. // to accomplish the multiplications
  114.  
  115. // use the fact that 320*y = 256*y + 64*y = y<<8 + y<<6
  116.  
  117. video_buffer[((y<<8) + (y<<6)) + x] = (unsigned char )color;
  118.  
  119. } // end Write_Pixel
  120.  
  121. ///////////////////////////////////////////////////////////////////////////////
  122.  
  123. int Read_Pixel(int x,int y)
  124. {
  125. // this function read a pixel from the screen buffer
  126.  
  127.  
  128. // use the fact that 320*y = 256*y + 64*y = y<<8 + y<<6
  129.  
  130. return((int)(video_buffer[((y<<8) + (y<<6)) + x]));
  131.  
  132. } // end Read_Pixel
  133.  
  134. //////////////////////////////////////////////////////////////////////////////
  135.  
  136. void Set_Graphics_Mode(int mode)
  137. {
  138.  
  139. // use the video interrupt 10h and the C interrupt function to set
  140. // the video mode
  141.  
  142. union REGS inregs,outregs;
  143.  
  144. inregs.h.ah = 0;                    // set video mode sub-function
  145. inregs.h.al = (unsigned char)mode;  // video mode to change to
  146.  
  147. _int86(0x10, &inregs, &outregs);
  148.  
  149. } // end Set_Graphics_Mode
  150.  
  151. /////////////////////////////////////////////////////////////////////////////
  152.  
  153. void Time_Delay(int clicks)
  154. {
  155. // this function uses the internal timer to wait a specified number of "clicks"
  156. // the actual amount of real time is the number of clicks * (time per click)
  157. // usually the time per click is set to 1/18th of a second or 55ms
  158.  
  159. long far *clock = (long far *)0x0000046CL; // address of timer
  160.  
  161. long start_time; // starting time
  162.  
  163. // get current time
  164.  
  165. start_time = *clock;
  166.  
  167. // when the current time minus the starting time >= the requested delay then
  168. // the function can exit
  169.  
  170. while(labs(*clock - start_time) < (long)clicks){}
  171.  
  172. } // end Time_Delay
  173.  
  174. ////////////////////////////////////////////////////////////////////////////////
  175.  
  176. void Line_H(int x1,int x2,int y,int color)
  177. {
  178. // draw a horizontal line using the memset function
  179. // this function does not do clipping hence x1,x2 and y must all be within
  180. // the bounds of the screen
  181.  
  182. int temp; // used for temporary storage during endpoint swap
  183.  
  184. // sort x1 and x2, so that x2 > x1
  185.  
  186. if (x1>x2)
  187.    {
  188.    temp = x1;
  189.    x1   = x2;
  190.    x2   = temp;
  191.    } // end swap
  192.  
  193. // draw the row of pixels
  194.  
  195. _fmemset((char far *)(video_buffer + ((y<<8) + (y<<6)) + x1),
  196.          (unsigned char)color,x2-x1+1);
  197.  
  198. } // end Line_H
  199.  
  200. //////////////////////////////////////////////////////////////////////////////
  201.  
  202. void Line_V(int y1,int y2,int x,int color)
  203. {
  204. // draw a vertical line, note that a memset function can no longer be
  205. // used since the pixel addresses are no longer contiguous in memory
  206. // note that the end points of the line must be on the screen
  207.  
  208. unsigned char far *start_offset; // starting memory offset of line
  209.  
  210. int index, // loop index
  211.     temp;  // used for temporary storage during swap
  212.  
  213. // make sure y2 > y1
  214.  
  215. if (y1>y2)
  216.    {
  217.    temp = y1;
  218.    y1   = y2;
  219.    y2   = temp;
  220.    } // end swap
  221.  
  222. // compute starting position
  223.  
  224. start_offset = video_buffer + ((y1<<8) + (y1<<6)) + x;
  225.  
  226. for (index=0; index<=y2-y1; index++)
  227.     {
  228.     // set the pixel
  229.  
  230.     *start_offset = (unsigned char)color;
  231.  
  232.     // move downward to next line
  233.  
  234.     start_offset+=320;
  235.  
  236.     } // end for index
  237.  
  238. } // end Line_V
  239.  
  240. //////////////////////////////////////////////////////////////////////////////
  241.  
  242. void Write_Color_Reg(int index, RGB_color_ptr color)
  243. {
  244.  
  245. // this function is used to write a color register with the RGB value
  246. // within "color"
  247.  
  248. // tell vga card which color register to update
  249.  
  250. _outp(COLOR_REGISTER_WR, index);
  251.  
  252. // update the color register RGB triple, note the same port is used each time
  253. // the hardware will make sure each of the components is stored in the
  254. // proper location
  255.  
  256. _outp(COLOR_DATA,color->red);
  257. _outp(COLOR_DATA,color->green);
  258. _outp(COLOR_DATA,color->blue);
  259.  
  260. } // end Write_Color_Reg
  261.  
  262. ///////////////////////////////////////////////////////////////////////////////
  263.  
  264. RGB_color_ptr Read_Color_Reg(int index, RGB_color_ptr color)
  265. {
  266.  
  267. // this function reads the RGB triple out of a palette register and places it
  268. // into "color"
  269.  
  270. // tell vga card which register to read
  271.  
  272. _outp(COLOR_REGISTER_RD, index);
  273.  
  274. // now extract the data
  275.  
  276. color->red   = (unsigned char)_inp(COLOR_DATA);
  277. color->green = (unsigned char)_inp(COLOR_DATA);
  278. color->blue  = (unsigned char)_inp(COLOR_DATA);
  279.  
  280. // return a pointer to color so that the function can be used as an RVALUE
  281.  
  282. return(color);
  283.  
  284. } // end Read_Color_Reg
  285.  
  286. ///////////////////////////////////////////////////////////////////////////////
  287.  
  288. void Read_Palette(int start_reg,int end_reg, RGB_palette_ptr the_palette)
  289. {
  290. // this function will read the palette registers in the range start_reg to
  291. // end_reg and save them into the appropriate positions in colors
  292.  
  293. int index; // used for looping
  294.  
  295. RGB_color color;
  296.  
  297. // read all the registers
  298.  
  299. for (index=start_reg; index<=end_reg; index++)
  300.     {
  301.     // read the color register
  302.  
  303.     Read_Color_Reg(index,(RGB_color_ptr)&color);
  304.  
  305.     // save it in database
  306.  
  307.     the_palette->colors[index].red   = color.red;
  308.     the_palette->colors[index].green = color.green;
  309.     the_palette->colors[index].blue  = color.blue;
  310.  
  311.     } // end for index
  312.  
  313. // save the interval of registers that were saved
  314.  
  315. the_palette->start_reg = start_reg;
  316. the_palette->end_reg   = end_reg;
  317.  
  318. } // end Read_Palette
  319.  
  320. ///////////////////////////////////////////////////////////////////////////////
  321.  
  322. void Write_Palette(int start_reg,int end_reg, RGB_palette_ptr the_palette)
  323. {
  324. // this function will write to the color registers using the data in the
  325. // sen palette structure
  326.  
  327. int index; // used for looping
  328.  
  329. // write all the registers
  330.  
  331. for (index=start_reg; index<=end_reg; index++)
  332.     {
  333.     // write the color registers using the data from the sent palette
  334.  
  335.     Write_Color_Reg(index,(RGB_color_ptr)&(the_palette->colors[index]));
  336.  
  337.     } // end for index
  338.  
  339. } // end Write_Palette
  340.  
  341. ///////////////////////////////////////////////////////////////////////////////
  342.  
  343. void Draw_Rectangle(int x1,int y1,int x2,int y2,int color)
  344. {
  345. // this function will draw a rectangle from (x1,y1) - (x2,y2)
  346. // it is assumed that each endpoint is within the screen boundaries
  347. // and (x1,y1) is the upper left hand corner and (x2,y2) is the lower
  348. // right hand corner
  349.  
  350. unsigned char far *start_offset;   // starting memory offset of first row
  351.  
  352. int width;   // width of rectangle
  353.  
  354. // compute starting offset of first row
  355.  
  356. start_offset = video_buffer + ((y1<<8) + (y1<<6)) + x1;
  357.  
  358. // compute width of rectangle
  359.  
  360. width  = 1 + x2 - x1; // the "1" to reflect the true width in pixels
  361.  
  362. // draw the rectangle row by row
  363.  
  364. while(y1++<=y2)
  365.      {
  366.      // draw the line
  367.  
  368.      _fmemset((char far *)start_offset,(unsigned char)color,width);
  369.  
  370.      // move the memory pointer to the next line
  371.  
  372.      start_offset+=320;
  373.  
  374.      } // end while
  375.  
  376. } // end Draw_Rectangle
  377.  
  378. ////////////////////////////////////////////////////////////////////////////////
  379.  
  380. void Fill_Screen(int color)
  381. {
  382. // this function will fill the entire screen with the sent color
  383.  
  384. // use the inline assembler for speed
  385.  
  386. _asm
  387.    {
  388.  
  389.    les di,video_buffer   ; point es:di to video buffer
  390.  
  391.    mov al,BYTE PTR color ; move the color into al and ah
  392.  
  393.    mov ah,al             ; replicate color into ah
  394.  
  395.    mov cx,320*200/2      ; number of words to fill(using word is faster than bytes)
  396.  
  397.    rep stosw             ; move the color into the video buffer really fast!
  398.  
  399.    } // end inline asm
  400.  
  401. } // end Fill_Screen
  402.  
  403. //////////////////////////////////////////////////////////////////////////////
  404.  
  405. void Fill_Screen_Z(int color)
  406. {
  407. // this function will fill the mode Z video buffer with the sent color
  408.  
  409. // use the inline assembler for speed
  410.  
  411. _asm
  412.    {
  413.    mov dx,SEQUENCER          ; address the sequencer
  414.    mov al,SEQ_PLANE_ENABLE   ; select the plane enable register
  415.    mov ah,0fh                ; enable all four planes
  416.    out dx,ax                 ; do it baby!
  417.    les di,video_buffer   ; point es:di to video buffer
  418.    mov al,BYTE PTR color ; move the color into al and ah
  419.    mov ah,al             ; replicate color into ah
  420.    mov cx,320*400/8      ; number of words to fill(using word is faster than bytes)
  421.    rep stosw             ; move the color into the video buffer really fast!
  422.    } // end inline asm
  423.  
  424. } // end Fill_Screen_Z
  425.  
  426. ///////////////////////////////////////////////////////////////////////////////
  427.  
  428. void Write_Pixel_Z(int x,int y,int color)
  429. {
  430.  
  431. // this function will write a pixel to screen in mode Z
  432.  
  433. // first select the proper color plane use inline for speed
  434. // if we used C then there would be a function call and about 10-15 more
  435. // instructions!
  436.  
  437. _asm
  438.    {
  439.    mov dx,SEQUENCER          ; address the sequencer
  440.    mov al,SEQ_PLANE_ENABLE   ; select the plane enable register
  441.    mov cl,BYTE PTR x         ; extract lower byte from x
  442.    and cl,03h                ; extract the plane number = x MOD 4
  443.    mov ah,1                  ; a "1" selects the plane in the plane enable
  444.    shl ah,cl                 ; shift the "1" bit proper number of times
  445.    out dx,ax                 ; do it baby!
  446.    } // end asm
  447.  
  448. // write the pixel, offset = (y*320+x)/4
  449.  
  450. video_buffer[(y<<6)+(y<<4)+(x>>2)] = (unsigned char )color;
  451.  
  452. } // end Write_Pixel_Z
  453.  
  454. ///////////////////////////////////////////////////////////////////////////////
  455.  
  456. void Set_Mode_Z(void)
  457. {
  458. // this function will set the video mode to 320x400x256
  459.  
  460. int data;  // used to store data
  461.  
  462. // set system to mode 13h and use it as a foundation to base 320x400 mode on
  463.  
  464. _asm
  465.    {
  466.    mov ax,0013h  ; ah=function number 00(set graphics mode), al=13h
  467.    int 10h       ; video interrupt 10h
  468.    } // end asm
  469.  
  470. // make changes to the crt controller first
  471.  
  472. // set number of scanlines to 1
  473.  
  474. _outp(CRT_CONTROLLER,CRT_MAX_SCANLINE);
  475. data=_inp(CRT_CONTROLLER+1);
  476. _outp(CRT_CONTROLLER+1,RESET_BITS(data,0x0f));
  477.  
  478. // use byte addressing instead of word
  479.  
  480. _outp(CRT_CONTROLLER,CRT_ADDR_MODE);
  481. data=_inp(CRT_CONTROLLER+1);
  482. _outp(CRT_CONTROLLER+1,RESET_BITS(data,0x40));
  483.  
  484. // second register that needs to reflect byte addressing
  485.  
  486. _outp(CRT_CONTROLLER,CRT_MODE_CONTROL);
  487. data=_inp(CRT_CONTROLLER+1);
  488. _outp(CRT_CONTROLLER+1,SET_BITS(data,0x40));
  489.  
  490. // make changes to graphics controller
  491.  
  492. // set addressing to not use odd/even memory writes
  493.  
  494. _outp(GFX_CONTROLLER,GFX_WRITE_MODE);
  495. data=_inp(GFX_CONTROLLER+1);
  496. _outp(GFX_CONTROLLER+1,RESET_BITS(data,0x10));
  497.  
  498. // don't chain the memory maps together
  499.  
  500. _outp(GFX_CONTROLLER,GFX_MISC);
  501. data=_inp(GFX_CONTROLLER+1);
  502. _outp(GFX_CONTROLLER+1,RESET_BITS(data,0x02));
  503.  
  504. // make changes to sequencer
  505.  
  506. // again we must select no chaining and no odd/even memory addressing
  507.  
  508. _outp(SEQUENCER,SEQ_MEMORY_MODE);
  509. data =_inp(SEQUENCER+1);
  510. data = RESET_BITS(data,0x08);
  511. data = SET_BITS(data,0x04);
  512. _outp(SEQUENCER+1,data);
  513.  
  514. // now clear the screen
  515.  
  516. _outp(SEQUENCER,SEQ_PLANE_ENABLE);
  517. _outp(SEQUENCER+1,0x0f);
  518.  
  519. // clear the screen, remember it is 320x400, but that is divided into four
  520. // planes, hence we need only to clear 32k out since there will ne four planes
  521. // each being cleared in parallel for a total of 4*32k or 128 = 320x400
  522. // note: "k" in this example means 1000 not 1024
  523.  
  524. _asm
  525.    {
  526.  
  527.    les di,video_buffer   ; point es:di to video buffer, same addre for mode Z
  528.    xor ax,ax             ; move a zero into al and ah
  529.    mov cx,320*400/8      ; number of words to fill(using word is faster than bytes)
  530.    rep stosw             ; move the color into the video buffer really fast!
  531.  
  532.    } // end inline asm
  533.  
  534. } // end Set_Mode_Z
  535.  
  536.  
  537.