home *** CD-ROM | disk | FTP | other *** search
/ Stars of Shareware: Programmierung / SOURCE.mdf / programm / msdos / c / rflic2 / pcclone.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-04-01  |  11.9 KB  |  500 lines

  1. /* pcclone.c - This file contains all the machine specific bits of the
  2.  * flic reader.  It's job is to set up data structures and routines for
  3.  * the Screen, Clock, and Key structures,  and the Machine structure
  4.  * that contains them all.
  5.  *
  6.  * For optimum performance a flic-reader should be coded in assembler.
  7.  * However you can get significantly greater performance merely by
  8.  * recoding in assembler the three routines: screen_copy_seg(), 
  9.  * screen_repeat_one() and screen_repeat_two().
  10.  *
  11.  * Copyright (c) 1992 Jim Kent.  This file may be freely used, modified,
  12.  * copied and distributed.  This file was first published as part of
  13.  * an article for Dr. Dobb's Journal March 1993 issue.
  14.  */
  15.  
  16. #include <bios.h>
  17. #include <dos.h>
  18. #include <time.h>
  19. #include <malloc.h>
  20. #ifdef _MSC_VER
  21. #include <memory.h> 
  22. #include <conio.h> 
  23. #else
  24. #include <mem.h> 
  25. #include <alloc.h>
  26. #endif
  27. #include <limits.h>
  28. #include <fcntl.h>
  29. #include <io.h>
  30. #include "types.h"
  31. #include "pcclone.h"
  32. #include "flic.h"
  33. #include "readflic.h"
  34.  
  35. /** Screen oriented stuff. **/
  36.  
  37. static Boolean set_vmode(Uchar mode);
  38. static Uchar get_vmode();
  39.  
  40. static Boolean set_vmode(Uchar mode)
  41.     /* Ask bios to set video mode. */
  42. {
  43. #ifdef _MSC_VER
  44.    union _REGS regs;
  45. #else
  46.    union REGS regs;
  47. #endif
  48.  
  49. regs.h.ah = 0;        /* Set Video Mode request. */
  50. regs.h.al = mode;    /* For our specific mode. */
  51. #ifdef _MSC_VER
  52.    _int86(0x10, ®s, ®s);
  53. #else
  54.    int86(0x10, ®s, ®s);
  55. #endif
  56. return TRUE;
  57. // return (regs.x.cflag == 0);    /* Carry flag clear? */
  58. }
  59.  
  60. static Uchar get_vmode()
  61.     /* Ask bios for current video mode. */
  62. {
  63. #ifdef _MSC_VER
  64.    union _REGS regs;
  65. #else
  66.    union REGS regs;
  67. #endif
  68.  
  69. regs.h.ah = 0xF;            /* Get Video Mode request. */
  70. #ifdef _MSC_VER
  71.    _int86(0x10, ®s, ®s);
  72. #else
  73.    int86(0x10, ®s, ®s);
  74. #endif
  75. return regs.h.al;
  76. }
  77.  
  78. ErrCode screen_open(Screen *s)
  79.     /* Put machine into graphics mode and fill out screen structure. */
  80. {
  81. ClearStruct(s);            /* Start in a known state... */
  82. s->old_mode = get_vmode();
  83. if (set_vmode(0x13))
  84.     {
  85.     if (get_vmode() == 0x13)
  86.         {
  87.         s->is_open = TRUE;    /* Now it's open. */
  88.         s->width = 320;
  89.         s->height = 200;
  90.         s->pixels = MK_FP(0xA000,0);    /* Base video screen address. */
  91.         return Success;
  92.         }
  93.     }
  94. /* If got to here have failed.  Restore old video mode and return
  95.  * failure code. */
  96. set_vmode(s->old_mode);
  97. return ErrDisplay;
  98. }
  99.  
  100. void screen_close(Screen *s)
  101.     /* Close screen.  Restore original display mode. */
  102. {
  103. if (s->is_open)        /* Don't do this twice... */
  104.     {
  105.     set_vmode(s->old_mode);
  106.     ClearStruct(s);        /* Discourage use after it's closed... */
  107.     }
  108. }
  109.  
  110. int screen_width(Screen *s)
  111.     /* Return width of screen. */
  112. {
  113. return s->width;
  114. }
  115.  
  116. int screen_height(Screen *s)
  117.     /* Return height of screen. */
  118. {
  119. return s->height;
  120. }
  121.  
  122. void screen_put_dot(Screen *s, int x, int y, Pixel color)
  123.     /* Set one dot. */
  124. {
  125.     /* First clip it. */
  126. if (x < 0 || y < 0 || x >= s->width || y >= s->height)
  127.     return;
  128.  
  129.     /* Then set it. */
  130. s->pixels[(unsigned)y * s->width + x] = color;
  131. }
  132.  
  133. static Boolean line_clip(Screen *s, int *px, int *py, int *pwidth)
  134.     /* Clip a horizontal line segment so that it fits on the screen.
  135.      * Return FALSE if clipped out entirely. */
  136. {
  137. int x = *px;
  138. int y = *py;
  139. int width = *pwidth;
  140. int xend = x + width;
  141.  
  142. if (y < 0 || y >= s->height || xend < 0 || x >= s->width)
  143.     return FALSE;    /* Clipped off screen. */
  144. if (x < 0)
  145.     {
  146.     *pwidth = width = width + x;        /* and shortens width. */
  147.     *px = 0;
  148.     }
  149. if (xend > s->width)
  150.     {
  151.     *pwidth = width = width - (xend - s->width);
  152.     }
  153. if (width < 0)
  154.     return FALSE;
  155. return TRUE;
  156. }
  157.  
  158. void screen_copy_seg(Screen *s, int x, int y, Pixel far *pixels, int count)
  159.     /* Copy pixels from memory into screen. */
  160. {
  161. Pixel far *pt;
  162. int unclipped_x = x;
  163. int dx;
  164.  
  165.     /* First let's do some clipping. */
  166. if (!line_clip(s, &x, &y, &count))
  167.     return;
  168.  
  169. dx = x - unclipped_x;    /* Clipping change in start position. */
  170. if (dx != 0)
  171.     pixels += dx;            /* Advance over clipped pixels. */
  172.  
  173.     /* Calculate start screen address. */
  174. pt = s->pixels + (unsigned)y * (unsigned)s->width + (unsigned)x;
  175.  
  176.     /* Copy pixels to display. */
  177. while (--count >= 0)
  178.     *pt++ = *pixels++;
  179. }
  180.  
  181. void screen_repeat_one(Screen *s, int x, int y, Pixel color, int count)
  182.     /* Draw a horizontal line of a solid color */
  183. {
  184. Pixel far *pt;
  185.  
  186.     /* First let's do some clipping. */
  187. if (!line_clip(s, &x, &y, &count))
  188.     return;
  189.  
  190.     /* Calculate start screen address. */
  191. pt = s->pixels + (unsigned)y * (unsigned)s->width + (unsigned)x;
  192.  
  193.     /* Repeat pixel on display. */
  194. while (--count >= 0)
  195.     *pt++ = color;
  196. }
  197.  
  198. void screen_repeat_two(Screen *s, int x, int y, Pixels2 pixels2, int count)
  199.     /* Repeat 2 pixels count times on screen. */
  200. {
  201. Pixels2 far *pt;
  202. int is_odd;
  203.  
  204.     /* First let's do some clipping. */
  205. count <<= 1;        /* Convert from word to pixel count. */
  206. if (!line_clip(s, &x, &y, &count))
  207.     return;
  208. is_odd = (count&1);        /* Did it turn odd after clipping?  Ack! */
  209. count >>= 1;            /* Convert back to word count. */
  210.  
  211.     /* Calculate start screen address. */
  212. pt = (Pixels2 far *)(s->pixels
  213. + (unsigned)y * (unsigned)s->width + (unsigned)x);
  214.  
  215. while (--count >= 0)    /* Go set screen 2 pixels at a time. */
  216.     *pt++ = pixels2;
  217.  
  218. if (is_odd)                /* Deal with pixel at end of screen if needed. */
  219.     {
  220.     Pixel far *end = (Pixel far *)pt;
  221.     *end = pixels2.pixels[0];
  222.     }
  223. }
  224.  
  225.  
  226. void screen_put_colors(Screen *s, int start, Color far *colors, int count)
  227.     /* Set count colors in color map starting at start.  RGB values
  228.      * go from 0 to 255. */
  229. {
  230. int end = start + count;
  231. int ix;
  232.  
  233. for (ix = start; ix < end; ++ix)
  234.     {
  235. #ifdef _MSC_VER
  236.     _outp(0x3C8, ix);
  237.     _outp(0x3C9, colors->r>>2);
  238.     _outp(0x3C9, colors->g>>2);
  239.     _outp(0x3C9, colors->b>>2);
  240. #else
  241.     outportb(0x3C8, ix);
  242.     outportb(0x3C9, colors->r>>2);
  243.     outportb(0x3C9, colors->g>>2);
  244.     outportb(0x3C9, colors->b>>2);
  245. #endif
  246.     ++colors;
  247.     }
  248. }
  249.  
  250. void screen_put_colors_64(Screen *s, int start, Color far *colors, int count)
  251.     /* Set count colors in color map starting at start.  RGB values
  252.      * go from 0 to 64. */
  253. {
  254. int end = start + count;
  255. int ix;
  256.  
  257. for (ix = start; ix < end; ++ix)
  258.     {
  259. #ifdef _MSC_VER
  260.     _outp(0x3C8, ix);
  261.     _outp(0x3C9, colors->r);
  262.     _outp(0x3C9, colors->g);
  263.     _outp(0x3C9, colors->b);
  264. #else
  265.     outportb(0x3C8, ix);
  266.     outportb(0x3C9, colors->r);
  267.     outportb(0x3C9, colors->g);
  268.     outportb(0x3C9, colors->b);
  269. #endif
  270.     ++colors;
  271.     }
  272. }
  273.  
  274. /** Clock oriented stuff. **/
  275.  
  276. #define CMODE    0x43
  277. #define CDATA    0x40
  278.  
  279. ErrCode clock_open(Clock *c)
  280.     /* Set up clock and store speed of clock.  */
  281. {
  282. c->speed = 4608;           /* Our peculiar speed.  */
  283. #ifdef _MSC_VER
  284.    _outp(CMODE, 0x34);  /* Change from divide by two to linear. */    
  285.    _outp(CDATA, 0);        /* Set period to highest available. */
  286.    _outp(CDATA, 0);
  287. #else
  288.    outportb(CMODE, 0x34);  /* Change from divide by two to linear. */    
  289.    outportb(CDATA, 0);        /* Set period to highest available. */
  290.    outportb(CDATA, 0);
  291. #endif
  292. return Success;
  293. }
  294.  
  295. void clock_close(Clock *c)
  296.     /* Return clock to normal. */
  297. {
  298. #ifdef _MSC_VER
  299.    _outp(CMODE, 0x36);  /* Change from linear to divide by two. */    
  300.    _outp(CDATA, 0);        /* Set period to highest available. */
  301.    _outp(CDATA, 0);
  302. #else
  303.    outportb(CMODE, 0x36);  /* Change from linear to divide by two. */    
  304.    outportb(CDATA, 0);        /* Set period to highest available. */
  305.    outportb(CDATA, 0);
  306. #endif
  307. }
  308.  
  309. Ulong clock_ticks(Clock *c)
  310.     /* Get current clock tick. */
  311. {
  312. /* This routine returns a clock with occassional spikes where time will
  313.  * look like its running backwards 1/18th of a second.  The resolution
  314.  * of the clock is 1/(18*256) = 1/4608 second.  The spikes are ok for
  315.  * our purposes since the wait loop will just ignore them. */
  316. #ifdef _MSC_VER
  317.    union _REGS regs;
  318. #else
  319.    union REGS regs;
  320. #endif
  321. Uchar chip_time;
  322. Ulong time;
  323.  
  324. regs.h.ah = 0;                /* Go ask BIOS timer services */
  325. #ifdef _MSC_VER
  326.    _int86(0x1A, ®s, ®s);    /* for time in 1/18ths second. */
  327.    _outp(CMODE,0);            /* Latch time at timer chip. */
  328.    _inp(CDATA);                /* Read in LSB of chip time and discard. */
  329.    chip_time = _inp(CDATA);    /* Read in MSB of chip time and save. */
  330. #else
  331.    int86(0x1A, ®s, ®s);    /* for time in 1/18ths second. */
  332.    outportb(CMODE,0);            /* Latch time at timer chip. */
  333.    inportb(CDATA);                /* Read in LSB of chip time and discard. */
  334.    chip_time = inportb(CDATA);    /* Read in MSB of chip time and save. */
  335. #endif
  336. chip_time = -(signed char)chip_time;
  337.     /* We calculate the time using 3 bytes from the BIOS 18hz counter
  338.      * and one byte from the timer chip itself.  We discard the hi
  339.      * byte of the BIOS time,  shift the rest left by 8, and
  340.      * fill in the low byte with the MSB from the chip timer.
  341.      * This looks a little more complicated than this because 
  342.      * the bios time is in various registers - cx for the hi word
  343.      * and dx for the low word. */
  344. time = (Ulong)regs.h.cl << 24L;        /* Get MSB of our final time. */
  345. time += (Ulong)regs.x.dx << 8L;        /* Fold in middle two bytes. */
  346. time += (Ulong)chip_time;            /* Add in LSB from chip. */
  347. return time;
  348. }
  349.  
  350.  
  351.  
  352. /** Keyboard oriented stuff. **/
  353.  
  354. ErrCode key_open(Key *key)
  355.     /* Set up keyboard. */
  356. {
  357. return Success;        /* Pretty easy on a PC. */
  358. }
  359.  
  360. void key_close(Key *key)
  361.     /* Close keyboard. */
  362. {
  363. return;                /* Also very easy under DOS. */
  364. }
  365.  
  366. Boolean key_ready(Key *key)
  367.     /* See if a key is ready. */
  368. {
  369. unsigned val;
  370.  
  371. if ((val = _bios_keybrd(_KEYBRD_READY)) == 0)
  372.     return FALSE;
  373. else
  374.     {
  375.     key->ascii = val;
  376.     key->scancode = val;
  377.     return TRUE;
  378.     }
  379. }
  380.  
  381. Uchar key_read(Key *key)
  382.     /* Get next key. */
  383. {
  384. unsigned val;
  385.  
  386. val = _bios_keybrd(_KEYBRD_READ);
  387. key->ascii = val;
  388. key->scancode = val;
  389. return key->ascii;
  390. }
  391.  
  392.  
  393. /** BigBlock stuff - to allocate and free blocks of memory > 64K. */
  394.  
  395. ErrCode big_alloc(BigBlock *bb, Ulong size)
  396.     /* Allocate a big block. */
  397. {
  398. #ifdef _MSC_VER
  399.    bb->hpt = _halloc(size, 1);
  400. #else
  401.    bb->fpt = farmalloc(size);
  402.    bb->hpt = bb->fpt;
  403. #endif
  404. if (!bb->hpt)
  405.     return ErrNoMemory;
  406. else
  407.     return Success;
  408. }
  409.  
  410. void big_free(BigBlock *bb)
  411.     /* Free up a big block. */
  412. {
  413. #ifdef _MSC_VER
  414.    if (bb->hpt != NULL)
  415.     {
  416.        _hfree(bb->hpt);
  417.        ClearStruct(bb);
  418.     }
  419. #else
  420.    if (bb->fpt != NULL)
  421.     {
  422.        farfree(bb->fpt);
  423.        ClearStruct(bb);
  424.    }
  425. #endif
  426. }
  427.  
  428. /** Stuff for reading files - regular and over 64k blocks at a time. **/
  429.  
  430. ErrCode file_open_to_read(FileHandle *phandle, char *name)
  431.     /* Open a binary file to read. */
  432. {
  433. #ifdef _MSC_VER
  434. if ((*phandle = _open(name, O_RDONLY|O_BINARY)) < 0)
  435. #else
  436. if ((*phandle = open(name, O_RDONLY|O_BINARY)) < 0)
  437. #endif
  438.     return ErrOpen;
  439. else
  440.     return Success;
  441. }
  442.  
  443. ErrCode file_read_block(FileHandle handle, void far *block, unsigned size)
  444.     /* Read in a block.  If read less than size return error code. */
  445. {
  446. unsigned size1 = size;
  447. if (_dos_read(handle, block, size1, &size1) != Success)
  448.     return ErrRead;
  449. else
  450.     return Success;
  451. }
  452.  
  453. ErrCode file_read_big_block(FileHandle handle, BigBlock *bb, Ulong size)
  454.     /* Read in a big block.  Could be bigger than 64K. */
  455. {
  456. char huge *pt = (char huge *)bb->hpt;
  457. unsigned size1;
  458. ErrCode err;
  459.  
  460. while (size != 0)
  461.     {
  462.     size1 = (unsigned)((size > 0xFFF0) ? 0xFFF0 : size);
  463.     if ((err = file_read_block(handle, pt, size1)) < Success)
  464.         return err;
  465.     pt += size1;        /* Advance pointer to next batch. */
  466.     size -= size1;        /* Subtract current batch from size to go. */
  467.     }
  468. return Success;
  469. }
  470.  
  471.  
  472. /** Machine oriented stuff - open and close the whole banana. **/
  473.  
  474. ErrCode machine_open(Machine *machine)
  475.     /* Open up machine: keyboard, clock, and screen. */
  476. {
  477. ErrCode err;
  478.  
  479. ClearStruct(machine);    /* Start it in a known state. */
  480. if ((err = key_open(&machine->key)) >= Success)
  481.     {
  482.     if ((err = clock_open(&machine->clock)) >= Success)
  483.         {
  484.         if ((err = screen_open(&machine->screen)) >= Success)
  485.             return Success;
  486.         clock_close(&machine->clock);
  487.         }
  488.     key_close(&machine->key);
  489.     }
  490. return err;
  491. }
  492.  
  493. void machine_close(Machine *machine)
  494.     /* Close down machine. */
  495. {
  496. screen_close(&machine->screen);
  497. clock_close(&machine->clock);
  498. key_close(&machine->key);
  499. }
  500.