home *** CD-ROM | disk | FTP | other *** search
- /* pcclone.c - This file contains all the machine specific bits of the
- * flic reader. It's job is to set up data structures and routines for
- * the Screen, Clock, and Key structures, and the Machine structure
- * that contains them all.
- *
- * For optimum performance a flic-reader should be coded in assembler.
- * However you can get significantly greater performance merely by
- * recoding in assembler the three routines: screen_copy_seg(),
- * screen_repeat_one() and screen_repeat_two().
- *
- * Copyright (c) 1992 Jim Kent. This file may be freely used, modified,
- * copied and distributed. This file was first published as part of
- * an article for Dr. Dobb's Journal March 1993 issue.
- */
-
- #include <bios.h>
- #include <dos.h>
- #include <time.h>
- #include <malloc.h>
- #ifdef _MSC_VER
- #include <memory.h>
- #include <conio.h>
- #else
- #include <mem.h>
- #include <alloc.h>
- #endif
- #include <limits.h>
- #include <fcntl.h>
- #include <io.h>
- #include "types.h"
- #include "pcclone.h"
- #include "flic.h"
- #include "readflic.h"
-
- /** Screen oriented stuff. **/
-
- static Boolean set_vmode(Uchar mode);
- static Uchar get_vmode();
-
- static Boolean set_vmode(Uchar mode)
- /* Ask bios to set video mode. */
- {
- #ifdef _MSC_VER
- union _REGS regs;
- #else
- union REGS regs;
- #endif
-
- regs.h.ah = 0; /* Set Video Mode request. */
- regs.h.al = mode; /* For our specific mode. */
- #ifdef _MSC_VER
- _int86(0x10, ®s, ®s);
- #else
- int86(0x10, ®s, ®s);
- #endif
- return TRUE;
- // return (regs.x.cflag == 0); /* Carry flag clear? */
- }
-
- static Uchar get_vmode()
- /* Ask bios for current video mode. */
- {
- #ifdef _MSC_VER
- union _REGS regs;
- #else
- union REGS regs;
- #endif
-
- regs.h.ah = 0xF; /* Get Video Mode request. */
- #ifdef _MSC_VER
- _int86(0x10, ®s, ®s);
- #else
- int86(0x10, ®s, ®s);
- #endif
- return regs.h.al;
- }
-
- ErrCode screen_open(Screen *s)
- /* Put machine into graphics mode and fill out screen structure. */
- {
- ClearStruct(s); /* Start in a known state... */
- s->old_mode = get_vmode();
- if (set_vmode(0x13))
- {
- if (get_vmode() == 0x13)
- {
- s->is_open = TRUE; /* Now it's open. */
- s->width = 320;
- s->height = 200;
- s->pixels = MK_FP(0xA000,0); /* Base video screen address. */
- return Success;
- }
- }
- /* If got to here have failed. Restore old video mode and return
- * failure code. */
- set_vmode(s->old_mode);
- return ErrDisplay;
- }
-
- void screen_close(Screen *s)
- /* Close screen. Restore original display mode. */
- {
- if (s->is_open) /* Don't do this twice... */
- {
- set_vmode(s->old_mode);
- ClearStruct(s); /* Discourage use after it's closed... */
- }
- }
-
- int screen_width(Screen *s)
- /* Return width of screen. */
- {
- return s->width;
- }
-
- int screen_height(Screen *s)
- /* Return height of screen. */
- {
- return s->height;
- }
-
- void screen_put_dot(Screen *s, int x, int y, Pixel color)
- /* Set one dot. */
- {
- /* First clip it. */
- if (x < 0 || y < 0 || x >= s->width || y >= s->height)
- return;
-
- /* Then set it. */
- s->pixels[(unsigned)y * s->width + x] = color;
- }
-
- static Boolean line_clip(Screen *s, int *px, int *py, int *pwidth)
- /* Clip a horizontal line segment so that it fits on the screen.
- * Return FALSE if clipped out entirely. */
- {
- int x = *px;
- int y = *py;
- int width = *pwidth;
- int xend = x + width;
-
- if (y < 0 || y >= s->height || xend < 0 || x >= s->width)
- return FALSE; /* Clipped off screen. */
- if (x < 0)
- {
- *pwidth = width = width + x; /* and shortens width. */
- *px = 0;
- }
- if (xend > s->width)
- {
- *pwidth = width = width - (xend - s->width);
- }
- if (width < 0)
- return FALSE;
- return TRUE;
- }
-
- void screen_copy_seg(Screen *s, int x, int y, Pixel far *pixels, int count)
- /* Copy pixels from memory into screen. */
- {
- Pixel far *pt;
- int unclipped_x = x;
- int dx;
-
- /* First let's do some clipping. */
- if (!line_clip(s, &x, &y, &count))
- return;
-
- dx = x - unclipped_x; /* Clipping change in start position. */
- if (dx != 0)
- pixels += dx; /* Advance over clipped pixels. */
-
- /* Calculate start screen address. */
- pt = s->pixels + (unsigned)y * (unsigned)s->width + (unsigned)x;
-
- /* Copy pixels to display. */
- while (--count >= 0)
- *pt++ = *pixels++;
- }
-
- void screen_repeat_one(Screen *s, int x, int y, Pixel color, int count)
- /* Draw a horizontal line of a solid color */
- {
- Pixel far *pt;
-
- /* First let's do some clipping. */
- if (!line_clip(s, &x, &y, &count))
- return;
-
- /* Calculate start screen address. */
- pt = s->pixels + (unsigned)y * (unsigned)s->width + (unsigned)x;
-
- /* Repeat pixel on display. */
- while (--count >= 0)
- *pt++ = color;
- }
-
- void screen_repeat_two(Screen *s, int x, int y, Pixels2 pixels2, int count)
- /* Repeat 2 pixels count times on screen. */
- {
- Pixels2 far *pt;
- int is_odd;
-
- /* First let's do some clipping. */
- count <<= 1; /* Convert from word to pixel count. */
- if (!line_clip(s, &x, &y, &count))
- return;
- is_odd = (count&1); /* Did it turn odd after clipping? Ack! */
- count >>= 1; /* Convert back to word count. */
-
- /* Calculate start screen address. */
- pt = (Pixels2 far *)(s->pixels
- + (unsigned)y * (unsigned)s->width + (unsigned)x);
-
- while (--count >= 0) /* Go set screen 2 pixels at a time. */
- *pt++ = pixels2;
-
- if (is_odd) /* Deal with pixel at end of screen if needed. */
- {
- Pixel far *end = (Pixel far *)pt;
- *end = pixels2.pixels[0];
- }
- }
-
-
- void screen_put_colors(Screen *s, int start, Color far *colors, int count)
- /* Set count colors in color map starting at start. RGB values
- * go from 0 to 255. */
- {
- int end = start + count;
- int ix;
-
- for (ix = start; ix < end; ++ix)
- {
- #ifdef _MSC_VER
- _outp(0x3C8, ix);
- _outp(0x3C9, colors->r>>2);
- _outp(0x3C9, colors->g>>2);
- _outp(0x3C9, colors->b>>2);
- #else
- outportb(0x3C8, ix);
- outportb(0x3C9, colors->r>>2);
- outportb(0x3C9, colors->g>>2);
- outportb(0x3C9, colors->b>>2);
- #endif
- ++colors;
- }
- }
-
- void screen_put_colors_64(Screen *s, int start, Color far *colors, int count)
- /* Set count colors in color map starting at start. RGB values
- * go from 0 to 64. */
- {
- int end = start + count;
- int ix;
-
- for (ix = start; ix < end; ++ix)
- {
- #ifdef _MSC_VER
- _outp(0x3C8, ix);
- _outp(0x3C9, colors->r);
- _outp(0x3C9, colors->g);
- _outp(0x3C9, colors->b);
- #else
- outportb(0x3C8, ix);
- outportb(0x3C9, colors->r);
- outportb(0x3C9, colors->g);
- outportb(0x3C9, colors->b);
- #endif
- ++colors;
- }
- }
-
- /** Clock oriented stuff. **/
-
- #define CMODE 0x43
- #define CDATA 0x40
-
- ErrCode clock_open(Clock *c)
- /* Set up clock and store speed of clock. */
- {
- c->speed = 4608; /* Our peculiar speed. */
- #ifdef _MSC_VER
- _outp(CMODE, 0x34); /* Change from divide by two to linear. */
- _outp(CDATA, 0); /* Set period to highest available. */
- _outp(CDATA, 0);
- #else
- outportb(CMODE, 0x34); /* Change from divide by two to linear. */
- outportb(CDATA, 0); /* Set period to highest available. */
- outportb(CDATA, 0);
- #endif
- return Success;
- }
-
- void clock_close(Clock *c)
- /* Return clock to normal. */
- {
- #ifdef _MSC_VER
- _outp(CMODE, 0x36); /* Change from linear to divide by two. */
- _outp(CDATA, 0); /* Set period to highest available. */
- _outp(CDATA, 0);
- #else
- outportb(CMODE, 0x36); /* Change from linear to divide by two. */
- outportb(CDATA, 0); /* Set period to highest available. */
- outportb(CDATA, 0);
- #endif
- }
-
- Ulong clock_ticks(Clock *c)
- /* Get current clock tick. */
- {
- /* This routine returns a clock with occassional spikes where time will
- * look like its running backwards 1/18th of a second. The resolution
- * of the clock is 1/(18*256) = 1/4608 second. The spikes are ok for
- * our purposes since the wait loop will just ignore them. */
- #ifdef _MSC_VER
- union _REGS regs;
- #else
- union REGS regs;
- #endif
- Uchar chip_time;
- Ulong time;
-
- regs.h.ah = 0; /* Go ask BIOS timer services */
- #ifdef _MSC_VER
- _int86(0x1A, ®s, ®s); /* for time in 1/18ths second. */
- _outp(CMODE,0); /* Latch time at timer chip. */
- _inp(CDATA); /* Read in LSB of chip time and discard. */
- chip_time = _inp(CDATA); /* Read in MSB of chip time and save. */
- #else
- int86(0x1A, ®s, ®s); /* for time in 1/18ths second. */
- outportb(CMODE,0); /* Latch time at timer chip. */
- inportb(CDATA); /* Read in LSB of chip time and discard. */
- chip_time = inportb(CDATA); /* Read in MSB of chip time and save. */
- #endif
- chip_time = -(signed char)chip_time;
- /* We calculate the time using 3 bytes from the BIOS 18hz counter
- * and one byte from the timer chip itself. We discard the hi
- * byte of the BIOS time, shift the rest left by 8, and
- * fill in the low byte with the MSB from the chip timer.
- * This looks a little more complicated than this because
- * the bios time is in various registers - cx for the hi word
- * and dx for the low word. */
- time = (Ulong)regs.h.cl << 24L; /* Get MSB of our final time. */
- time += (Ulong)regs.x.dx << 8L; /* Fold in middle two bytes. */
- time += (Ulong)chip_time; /* Add in LSB from chip. */
- return time;
- }
-
-
-
- /** Keyboard oriented stuff. **/
-
- ErrCode key_open(Key *key)
- /* Set up keyboard. */
- {
- return Success; /* Pretty easy on a PC. */
- }
-
- void key_close(Key *key)
- /* Close keyboard. */
- {
- return; /* Also very easy under DOS. */
- }
-
- Boolean key_ready(Key *key)
- /* See if a key is ready. */
- {
- unsigned val;
-
- if ((val = _bios_keybrd(_KEYBRD_READY)) == 0)
- return FALSE;
- else
- {
- key->ascii = val;
- key->scancode = val;
- return TRUE;
- }
- }
-
- Uchar key_read(Key *key)
- /* Get next key. */
- {
- unsigned val;
-
- val = _bios_keybrd(_KEYBRD_READ);
- key->ascii = val;
- key->scancode = val;
- return key->ascii;
- }
-
-
- /** BigBlock stuff - to allocate and free blocks of memory > 64K. */
-
- ErrCode big_alloc(BigBlock *bb, Ulong size)
- /* Allocate a big block. */
- {
- #ifdef _MSC_VER
- bb->hpt = _halloc(size, 1);
- #else
- bb->fpt = farmalloc(size);
- bb->hpt = bb->fpt;
- #endif
- if (!bb->hpt)
- return ErrNoMemory;
- else
- return Success;
- }
-
- void big_free(BigBlock *bb)
- /* Free up a big block. */
- {
- #ifdef _MSC_VER
- if (bb->hpt != NULL)
- {
- _hfree(bb->hpt);
- ClearStruct(bb);
- }
- #else
- if (bb->fpt != NULL)
- {
- farfree(bb->fpt);
- ClearStruct(bb);
- }
- #endif
- }
-
- /** Stuff for reading files - regular and over 64k blocks at a time. **/
-
- ErrCode file_open_to_read(FileHandle *phandle, char *name)
- /* Open a binary file to read. */
- {
- #ifdef _MSC_VER
- if ((*phandle = _open(name, O_RDONLY|O_BINARY)) < 0)
- #else
- if ((*phandle = open(name, O_RDONLY|O_BINARY)) < 0)
- #endif
- return ErrOpen;
- else
- return Success;
- }
-
- ErrCode file_read_block(FileHandle handle, void far *block, unsigned size)
- /* Read in a block. If read less than size return error code. */
- {
- unsigned size1 = size;
- if (_dos_read(handle, block, size1, &size1) != Success)
- return ErrRead;
- else
- return Success;
- }
-
- ErrCode file_read_big_block(FileHandle handle, BigBlock *bb, Ulong size)
- /* Read in a big block. Could be bigger than 64K. */
- {
- char huge *pt = (char huge *)bb->hpt;
- unsigned size1;
- ErrCode err;
-
- while (size != 0)
- {
- size1 = (unsigned)((size > 0xFFF0) ? 0xFFF0 : size);
- if ((err = file_read_block(handle, pt, size1)) < Success)
- return err;
- pt += size1; /* Advance pointer to next batch. */
- size -= size1; /* Subtract current batch from size to go. */
- }
- return Success;
- }
-
-
- /** Machine oriented stuff - open and close the whole banana. **/
-
- ErrCode machine_open(Machine *machine)
- /* Open up machine: keyboard, clock, and screen. */
- {
- ErrCode err;
-
- ClearStruct(machine); /* Start it in a known state. */
- if ((err = key_open(&machine->key)) >= Success)
- {
- if ((err = clock_open(&machine->clock)) >= Success)
- {
- if ((err = screen_open(&machine->screen)) >= Success)
- return Success;
- clock_close(&machine->clock);
- }
- key_close(&machine->key);
- }
- return err;
- }
-
- void machine_close(Machine *machine)
- /* Close down machine. */
- {
- screen_close(&machine->screen);
- clock_close(&machine->clock);
- key_close(&machine->key);
- }
-