/*
pixel3.c: Masanori HATA <http://www.mihr.net/>
[original] pixel.c: David Brackeen <http://www.brackeen.com/home/vga/>
*/

#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <sys/nearptr.h>
#include <sys/farptr.h>
#include <go32.h>
#include <sys/movedata.h>

#define VIDEO_INT           0x10      /* the BIOS video interrupt. */
#define WRITE_DOT           0x0C      /* BIOS func to plot a pixel. */
#define SET_MODE            0x00      /* BIOS func to set the video mode. */
#define VGA_256_COLOR_MODE  0x13      /* use to set 256-color mode. */
#define TEXT_MODE           0x03      /* use to set 80x25 text mode. */

#define SCREEN_WIDTH        320       /* width  in pixels of mode 0x13 */
#define SCREEN_HEIGHT       200       /* height in pixels of mode 0x13 */
#define NUM_COLORS          256       /* number of colors in mode 0x13 */

#define LOOP                10000000  /* number of loops for plotting pixels */

typedef unsigned char  byte;
typedef unsigned short word;

byte *VGA      = (byte *)0xA0000;          /* this points to video memory. */
word *my_clock = (word *)0x046C;      /* this points to the 18.2hz system clock. */

void set_mode(byte mode)
{
  union REGS regs;

  regs.h.ah = SET_MODE;
  regs.h.al = mode;
  int86(VIDEO_INT, &regs, &regs);
}

void bios_plot_pixel(int x, int y ,byte color)
{
  union REGS regs;

  regs.h.ah = WRITE_DOT;
  regs.h.al = color;
  regs.x.cx = x;
  regs.x.dx = y;
  int86(VIDEO_INT, &regs, &regs);
}

void memput_plot_pixel(int x, int y, byte color) {
  _dosmemputb(&color, 1, 0xa0000 + y * SCREEN_WIDTH + x);
}

void farptr_plot_pixel(int x, int y, byte color)
{
  _farpokeb(_dos_ds, 0xa0000 + y * SCREEN_WIDTH + x, color);
}

void nearptr_plot_pixel(int x, int y, byte color)
{
  VGA[y * SCREEN_WIDTH + x]=color;
}

void main()
{
  int x, y, color;
  float t1, t2, t3, t4;
  word start;
  unsigned long i;

  if (__djgpp_nearptr_enable() == 0)
  {
    printf("Could get access to first 640K of memory.\n");
    exit(-1);
  }

  VGA += __djgpp_conventional_base;
  my_clock = (void *)my_clock + __djgpp_conventional_base;

  srand(*my_clock);                   /* seed the number generator. */

  set_mode(VGA_256_COLOR_MODE);       /* set the video mode. */
  start = *my_clock;                  /* record the starting time. */
  for(i = 0; i < LOOP / 100; i++)
  {
    x     = rand() % SCREEN_WIDTH;
    y     = rand() % SCREEN_HEIGHT;
    color = rand() % NUM_COLORS;
    bios_plot_pixel(x, y, color);
  }
  t1 = (*my_clock - start) / 18.2 * 100; /* calculate how long it took. */

  set_mode(VGA_256_COLOR_MODE);       /* set the video mode again in order to clear the screen. */
  start = *my_clock;                  /* record the starting time. */
  for(i = 0; i < LOOP; i++)
  {
    x     = rand() % SCREEN_WIDTH;
    y     = rand() % SCREEN_HEIGHT;
    color = rand() % NUM_COLORS;
    memput_plot_pixel(x, y, color);
  }
  t2 = (*my_clock - start) / 18.2;    /* calculate how long it took. */

  set_mode(VGA_256_COLOR_MODE);       /* set the video mode again in order to clear the screen. */
  start = *my_clock;                  /* record the starting time. */
  for(i = 0; i < LOOP; i++)
  {
    x     = rand() % SCREEN_WIDTH;
    y     = rand() % SCREEN_HEIGHT;
    color = rand() % NUM_COLORS;
    farptr_plot_pixel(x, y, color);
  }
  t3 = (*my_clock - start) / 18.2;    /* calculate how long it took. */

  set_mode(VGA_256_COLOR_MODE);       /* set the video mode again in order to clear the screen. */
  start = *my_clock;                  /* record the starting time. */
  for(i = 0; i < LOOP; i++)
  {
    x     = rand() % SCREEN_WIDTH;
    y     = rand() % SCREEN_HEIGHT;
    color = rand() % NUM_COLORS;
    nearptr_plot_pixel(x, y, color);
  }
  t4 = (*my_clock - start) / 18.2;    /* calculate how long it took. */

  set_mode(TEXT_MODE);                /* set the video mode back to text mode. */

  /* output the results... */
  printf("BIOS         pixel plotting took %f seconds. (x%f)\n", t1, 1.0);
  printf("memput       pixel plotting took %f seconds. (x%f)\n", t2, t1 / t2);
  printf("far  pointer pixel plotting took %f seconds. (x%f)\n", t3, t1 / t3);
  printf("near pointer pixel plotting took %f seconds. (x%f)\n", t4, t1 / t4);

  __djgpp_nearptr_disable();

  return;
}


