Subject: Apple ][ color hires support added
From: "Axel Bauer" <[email-masked]>
To: [email-masked]
Date: Fri, 7 Jul 1995 13:12:01 +0100

Hello,

i added color hires support for your apple ][ emulator. it is not
a very clean solution, as it needs another 256 pixmaps, and does
not produce 100% correct output. but it looks good with all the
games i tried. the speed is the same as with monochrome hires.
a better solution would be to have just one large hires pixmap
in the server to reduce the overhead when creating the pixmaps and
save server memory.

i included a modified version of your "video.c" module. i compiled
it on SGI with IRIX 5.3 and the SGI compiler.
the speed is okay with a SGI Indy.
but i haven't had the source code with the compiler you did.
so the speed is not as good as with the binary versions you
distribute.

could you send me a source code version including your compiler ?
if not, could you prepare an executable with my version of "video.c"
compiled in ?

thank you in advance,
Axel

PS: i will improve the graphics output as i find some time and send
    it to you.

--
..............................................................
 a x e l   b a u e r               http://jones.netplace.com/

The sooner our happiness together begins, the longer it will last.
		-- Miramanee, "The Paradise Syndrome", stardate 4842.6

/*
 *	video.c
 *	Apple II video simulation
 */
#define	_VIDEO_C_

#include <stdio.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "hardware.h"
#include "memory.h"
#include "x_window.h"
#include "fonts/lowres.bmp"

#define	VIDEO_TEXT_MODE		0x01
#define	VIDEO_MIXED_MODE	0x02
#define	VIDEO_PAGE_2		0x04
#define	VIDEO_HIRES_MODE	0x08

#define	_IS_TEXT_MODE_(m)	((m)&VIDEO_TEXT_MODE)
#define	_IS_GRAPHICS_MODE_(m)	(!((m)&VIDEO_TEXT_MODE))
#define	_IS_FULLSCREEN_(m)	(!((m)&VIDEO_MIXED_MODE))
#define	_IS_MIXED_MODE_(m)	((m)&VIDEO_MIXED_MODE)
#define	_IS_PAGE_1_(m)		(!((m)&VIDEO_PAGE_2))
#define	_IS_PAGE_2_(m)		((m)&VIDEO_PAGE_2)
#define	_IS_LOWRES_MODE_(m)	(!((m)&VIDEO_HIRES_MODE))
#define	_IS_HIRES_MODE_(m)	((m)&VIDEO_HIRES_MODE)

#define	_SET_TEXT_MODE_(m)	((m)|=VIDEO_TEXT_MODE)
#define	_SET_GRAPHICS_MODE_(m)	((m)&=~VIDEO_TEXT_MODE)
#define	_SET_FULLSCREEN_(m)	((m)&=~VIDEO_MIXED_MODE)
#define	_SET_MIXED_MODE_(m)	((m)|=VIDEO_MIXED_MODE)
#define	_SET_PAGE_1_(m)		((m)&=~VIDEO_PAGE_2)
#define	_SET_PAGE_2_(m)		((m)|=VIDEO_PAGE_2)
#define	_SET_LOWRES_MODE_(m)	((m)&=~VIDEO_HIRES_MODE)
#define	_SET_HIRES_MODE_(m)	((m)|=VIDEO_HIRES_MODE)

extern char *bitmap_array[]; /* from fonts.c */

/*
 *	pixmap arrays
 */
Pixmap Apple_40_column_fonts[256];

Pixmap lowres_graphics_fonts[256];

/*
 * for color hires support, we need twice the pixmaps
 */

#ifdef COLOR
Pixmap hires_graphics_pixmap[512];

#else
Pixmap hires_graphics_pixmap[256];

#endif

/*
 *	low resolution graphics colors
 */
char *lowres_color_names[16] = {
   "Black",
   "Red",
   "NavyBlue",
   "Magenta",
   "DarkGreen",
   "Gray",
   "MediumBlue",
   "LightBlue",
   "Brown",
   "Orange",
   "Gray",
   "Pink",
   "Green",
   "Yellow",
   "Aquamarine",
   "White"
};

long lowres_color_pixels[16];

/*
 * mappings  for color hires support
*/

#ifdef COLOR
#define COLOR_INDEX_BLACK  lowres_color_pixels[0]
#define COLOR_INDEX_WHITE  lowres_color_pixels[15]
#define COLOR_INDEX_RED    lowres_color_pixels[1]
#define COLOR_INDEX_BLUE   lowres_color_pixels[7]
#define COLOR_INDEX_VIOLET lowres_color_pixels[11]
#define COLOR_INDEX_GREEN  lowres_color_pixels[12]
#endif

static int Video_Mode;

static int Last_Mode;

static int Change_Scheduled = 0;

static int Text_Lowres_Top;

/*
 *	to facilate fast buffer swapping
 *	two off screen pixmap are used for
 *	High resolution graphics
 */
Pixmap Hires_Page_1_Pixmap;

Pixmap Hires_Page_2_Pixmap;

/*
 * Dirty Video RAM Queue
 * To save overheads in high resolution video updates,
 * write to inactive hires pages are stored in a queue
 * updated in background. Memory location updated but not
 * stored in the buffer pixmaps are marked in the Dirty VRAM
 * array and its address stored in the Dirty Address Queue
 *
 * As MAX_DIRTY_QUEUE_SIZE is large then 40*192, so a
 * page can be held in a queue, Therefore we don't need to
 * check for queue overflow. Also mod MAX_QUEUE_SIZE can be
 * calculated easily if MAX_DIRTY_QUEUE_SIZE is a power of 2
 */
#define	MAX_DIRTY_QUEUE_SIZE	0x2000
#define	MOD_MAX_QUEUE_SIZE(x)	((x)&0x1FFF)

BYTE Page1_Dirty[0x2000];    /* for address 0x2000-0x3FFF */

int Page1_Queue_Head;

int Page1_Queue_Size;

ADDR Page1_Address_Queue[MAX_DIRTY_QUEUE_SIZE];

BYTE Page2_Dirty[0x2000];    /* for address 0x4000-0x5FFF */

int Page2_Queue_Head;

int Page2_Queue_Size;

ADDR Page2_Address_Queue[MAX_DIRTY_QUEUE_SIZE];

/*
 * Video refresh is done by periodically calling
 * video_refresh, using the micro job queue
 */
#define	DEFAULT_VIDEO_REFRESH_INTERVAL	80000	/* in TICKS */
int Video_Refresh_Interval = DEFAULT_VIDEO_REFRESH_INTERVAL;

Pixmap *text_lowres_line_fonts[24] = {
   Apple_40_column_fonts, Apple_40_column_fonts,
   Apple_40_column_fonts, Apple_40_column_fonts,
   Apple_40_column_fonts, Apple_40_column_fonts,
   Apple_40_column_fonts, Apple_40_column_fonts,
   Apple_40_column_fonts, Apple_40_column_fonts,
   Apple_40_column_fonts, Apple_40_column_fonts,
   Apple_40_column_fonts, Apple_40_column_fonts,
   Apple_40_column_fonts, Apple_40_column_fonts,
   Apple_40_column_fonts, Apple_40_column_fonts,
   Apple_40_column_fonts, Apple_40_column_fonts,
   Apple_40_column_fonts, Apple_40_column_fonts,
Apple_40_column_fonts, Apple_40_column_fonts};

/*
 *	base address offset of text lines
 */
int line_base_offset[24] = {
   0x0000, 0x0080, 0x0100, 0x0180,
   0x0200, 0x0280, 0x0300, 0x0380,
   0x0028, 0x00A8, 0x0128, 0x01A8,
   0x0228, 0x02A8, 0x0328, 0x03A8,
   0x0050, 0x00D0, 0x0150, 0x01D0,
0x0250, 0x02D0, 0x0350, 0x03D0};

void init_fonts();

void init_lowres();

void init_hires();

void Write_Hires_Video( /* ADDR addr, BYTE data */ );

void change_video_mode();

void copy_screen_to_pixmap();

void video_refresh();

void Refresh_Screen();

void Redraw_Screen();

void Redraw_Hires_Screen();

void
init_video()
{

 /*
  * initialize subsystems
  */
   init_fonts();

   init_lowres();
   init_hires();

 /*
  * start up video with page 1 text mode
  */
   _SET_TEXT_MODE_(Video_Mode);
   _SET_PAGE_1_(Video_Mode);
   Last_Mode = Video_Mode;
   Redraw_Screen(0, 23);
   XFlush(theDisplay);

 /*
  * setup video update micro job
  */
   submit_micro_job(AppleClock + 1, video_refresh, 0);

}

/*
 *	init_fonts: build 256 X-Pixmaps for Apple fonts
 */
void
init_fonts()
{
   char buffer[32];

   char *font_bitmap, unexpanded;

   int i, j, k;

   unsigned expanded;

   long pixmap_foreground, pixmap_background;

   Pixmap font_pixmap;

 /*
  * initialize the apple 40 column fonts use font data in bitmaps and expand
  * them to size 14 x 16
  */
   for (i = 0; i < 0x100; i++)
   {
      if (i < 0xe0)
	 font_bitmap = bitmap_array[i & 0x3f];
      else
	 font_bitmap = bitmap_array[i - 0xa0];
      for (j = 0; j < 8; j++)
      {
	 expanded = 0;
	 unexpanded = font_bitmap[j];
	 for (k = 0; k < 8; k++)
	 {
	    expanded >>= 2;
	    if (unexpanded & 0x01)
	       expanded |= 0xc000;
	    unexpanded >>= 1;
	 }
	 buffer[j * 4] = expanded & 0xff;
	 buffer[j * 4 + 1] = expanded >> 8;
	 buffer[j * 4 + 2] = expanded & 0xff;
	 buffer[j * 4 + 3] = expanded >> 8;
      }
      if (i < 0x40)
      {
      /* inverse */
	 pixmap_foreground = theBlackPixel;
	 pixmap_background = theWhitePixel;
      }
      else if (i < 0x80)
      {
      /* flash	 */
	 pixmap_foreground = theBlackPixel;
	 pixmap_background = theWhitePixel;
      }
      else
      {
      /* normal	 */
	 pixmap_foreground = theWhitePixel;
	 pixmap_background = theBlackPixel;
      }

      Apple_40_column_fonts[i] = XCreatePixmapFromBitmapData(
				       theDisplay, theWindow, buffer, 14, 16,
					pixmap_foreground, pixmap_background,
							     theScreenDepth);
   }
}

/*
 *	init_lowres(): build 256 X-Pixmaps for lowres graphic fonts
 */
void
init_lowres()
{
   int i, success;

   int lower_nibble, higher_nibble;

   XColor hardware_color, rgb_color;

/*
 *	allocate 16 Xcolor for
 *	low-resolution graphics
 */
   for (i = 0; i < 16; i++)
   {
      success = XLookupColor(theDisplay,
			     DefaultColormap(theDisplay, theScreen),
			     lowres_color_names[i],
			     &rgb_color, &hardware_color);

      if (success)
	 success &= XAllocColor(theDisplay,
				DefaultColormap(theDisplay, theScreen),
				&hardware_color);
      lowres_color_pixels[i] = hardware_color.pixel;

      if (!success)
      {
	 fprintf(stderr, "Cannot color number %d (%s)\n",
		 i, lowres_color_names[i]);
	 quit_X();
	 exit(1);
      }
   }

/*
 *	Create Pixmap for low resolution graphics
 */
   for (i = 0; i < 256; i++)
   {
      lower_nibble = i & 0x0f;
      higher_nibble = i >> 4;
      lowres_graphics_fonts[i] = XCreatePixmapFromBitmapData(
						       theDisplay, theWindow,
			   (char *) lowres_bits, lowres_width, lowres_height,
					   lowres_color_pixels[lower_nibble],
					  lowres_color_pixels[higher_nibble],
							     theScreenDepth);
   }
}

/*
 *	init_hires: build 256 X-Pixmaps for Apple fonts
 */
void
init_hires()
{
   char buffer[4];

   char unexpanded;

   int i, j, old;

   unsigned expanded;

/*
 * initialize the apple 40 column fonts
 * use font data in bitmaps and expand them
 * to size 14 x 16
 */

#ifdef COLOR
   for (i = 0; i < 512; i++)
   {
      unsigned int byte = i;
      int last = 0;
      unsigned int sbyte;

      register int j;
      GC  gc;

      if (byte > 255)
	 byte -= 256;
      sbyte = byte;

      hires_graphics_pixmap[i] = XCreatePixmap(theDisplay,
					   theWindow, 14, 2, theScreenDepth);

      gc = XCreateGC(theDisplay, hires_graphics_pixmap[i],
		     (unsigned long) 0, 0);

      XSetForeground(theDisplay, gc, COLOR_INDEX_BLACK);
      XFillRectangle(theDisplay, hires_graphics_pixmap[i],
		     gc, 0, 0, 14, 2);

      last = 0;

      for (j = 0; j < 7; j++)
      {
	 if (sbyte & 0x01)
	 {
	    if (last || ((j == 0) && (sbyte &0x02)))
	       XSetForeground(theDisplay, gc, COLOR_INDEX_WHITE);
	    else
	    {
	       if (byte & 0x80)
	       {
		  if (j % 2)
		     XSetForeground(theDisplay, gc,
			    (i > 0xFF) ? COLOR_INDEX_RED : COLOR_INDEX_BLUE);
		  else
		     XSetForeground(theDisplay, gc,
			    (i > 0xFF) ? COLOR_INDEX_BLUE : COLOR_INDEX_RED);
	       }
	       else
	       {
		  if (j % 2)
		     XSetForeground(theDisplay, gc,
			(i > 0xFF) ? COLOR_INDEX_GREEN : COLOR_INDEX_VIOLET);
		  else
		     XSetForeground(theDisplay, gc,
			(i > 0xFF) ? COLOR_INDEX_VIOLET : COLOR_INDEX_GREEN);
	       }
	    }

	    XFillRectangle(theDisplay, hires_graphics_pixmap[i],
			   gc, j * 2, 0, 2, 2);
	    last = 1;
	 }
	 else
	    last = 0;

	 sbyte >>= 1;
      }
      XFreeGC(theDisplay, gc);
   }
#else
   for (i = 0; i < 0x100; i++)
   {
      expanded = 0;
      unexpanded = i & 0x7f;
      for (j = 0; j < 7; j++)
      {
	 expanded >>= 2;
	 if (unexpanded & 0x01)
	    expanded |= 0x3000;
	 unexpanded >>= 1;
      }

      buffer[0] = expanded & 0xff;
      buffer[1] = expanded >> 8;
      buffer[2] = expanded & 0xff;
      buffer[3] = expanded >> 8;

      hires_graphics_pixmap[i] = XCreatePixmapFromBitmapData(
					theDisplay, theWindow, buffer, 14, 2,
						theWhitePixel, theBlackPixel,
							     theScreenDepth);
   }
#endif

/*
 * build offscreen hires buffer pixmaps
 */
   Hires_Page_1_Pixmap = XCreatePixmap(theDisplay, theWindow,
				SCREEN_WIDTH, SCREEN_HEIGHT, theScreenDepth);
   Hires_Page_2_Pixmap = XCreatePixmap(theDisplay, theWindow,
				SCREEN_WIDTH, SCREEN_HEIGHT, theScreenDepth);
   Hires_Tag = NO_DISPLAY;

/*
 * initialize dirty address queue
 */
   Page1_Queue_Head = 0;
   Page1_Queue_Size = 0;
   Page2_Queue_Head = 0;
   Page2_Queue_Size = 0;
   for (i = 0; i < 0x2000; i++)
   {
      Page1_Dirty[i] = 0;
      Page2_Dirty[i] = 0;
   }

/*
 * initialize buffer pixmap
 * by putting address of VRAM in to queue
 */
   for (i = 0x2000; i <= 0x5FFF; i++)
   {
      old = MainMemoryRAM[i >> 8][i & 0xff];
      MainMemoryRAM[i >> 8][i & 0xff] = ~old;
      Write_Hires_Video(i, old);
   }

}

void
video_refresh()
{

   if (Video_Mode != Last_Mode)
      change_video_mode();
   else
   {
      Flush_Dirty_Queue(1, 10);
      Flush_Dirty_Queue(2, 10);
   }

/*
 * reschedule itself
 */
   submit_micro_job(AppleClock + Video_Refresh_Interval,
		    video_refresh, 0);

}

void
toggle_video_mode_switch(address)
   ADDR address;
{
   if (!Change_Scheduled)
   {
   /* change mode 100 cycle later */
      submit_micro_job(AppleClock + 100, change_video_mode, 0);
      Change_Scheduled = 1;
   }

   switch (address)
   {
    case 0xC050:
      _SET_GRAPHICS_MODE_(Video_Mode);
      break;
    case 0xC051:
      _SET_TEXT_MODE_(Video_Mode);
      break;
    case 0xC052:
      _SET_FULLSCREEN_(Video_Mode);
      break;
    case 0xC053:
      _SET_MIXED_MODE_(Video_Mode);
      break;
    case 0xC054:
      _SET_PAGE_1_(Video_Mode);
      break;
    case 0xC055:
      _SET_PAGE_2_(Video_Mode);
      break;
    case 0xC056:
      _SET_LOWRES_MODE_(Video_Mode);
      break;
    case 0xC057:
      _SET_HIRES_MODE_(Video_Mode);
      break;
   }

}

void
change_video_mode()
{
   int i, start_row, end_row;

/*
 * reset Change_Schedule, so that later changes
 * can be processed
 */
   Change_Scheduled = 0;

/*
 * If we have backing store, we need to copy content of
 * window to screen buffer pixmap, so that change will
 * be recorded
 */

   if (HasBackingStore != NotUseful)
      copy_screen_to_pixmap();

/*
 * set page tags & text/lowres top
 */
   if (_IS_GRAPHICS_MODE_(Video_Mode) && _IS_HIRES_MODE_(Video_Mode))
   {
      Hires_Tag = _IS_PAGE_1_(Video_Mode) ? HIRES_PAGE1_ADDRESS :
	  HIRES_PAGE2_ADDRESS;
      if (_IS_MIXED_MODE_(Video_Mode))
      {
	 Text_Lowres_Top = 20;
	 Text_Lowres_Tag = _IS_PAGE_1_(Video_Mode) ? TEXT_PAGE1_ADDRESS :
	     TEXT_PAGE2_ADDRESS;
      }
      else
      {
	 Text_Lowres_Top = 24;
	 Text_Lowres_Tag = NO_DISPLAY;
      }
   }
   else
   {
      Hires_Tag = NO_DISPLAY;
      Text_Lowres_Top = 0;
      Text_Lowres_Tag = _IS_PAGE_1_(Video_Mode) ? TEXT_PAGE1_ADDRESS :
	  TEXT_PAGE2_ADDRESS;

   }

/*
 * set up text_lowres_line_font
 */
   if (_IS_TEXT_MODE_(Video_Mode))
   {
      for (i = 0; i < 24; i++)
	 text_lowres_line_fonts[i] = Apple_40_column_fonts;
   }
   else
   {
      for (i = 0; i < 20; i++)
	 text_lowres_line_fonts[i] = lowres_graphics_fonts;
      if (_IS_MIXED_MODE_(Video_Mode))
	 for (i = 20; i < 24; i++)
	    text_lowres_line_fonts[i] = Apple_40_column_fonts;
      else
	 for (i = 20; i < 24; i++)
	    text_lowres_line_fonts[i] = lowres_graphics_fonts;
   }

   Last_Mode = Video_Mode;
   Refresh_Screen();

/*
 * make all change visible
 */
   XFlush(theDisplay);

}

void
copy_screen_to_pixmap()
{
   Pixmap buffer_pixmap;

   int scan_lines;

   if (_IS_GRAPHICS_MODE_(Last_Mode) && _IS_HIRES_MODE_(Last_Mode))
   {
      buffer_pixmap = _IS_PAGE_1_(Last_Mode) ? Hires_Page_1_Pixmap :
	  Hires_Page_2_Pixmap;
      scan_lines = _IS_MIXED_MODE_(Last_Mode) ? 320 : 384;
      XCopyArea(theDisplay, theWindow, buffer_pixmap,
		theGC, 0, 0, SCREEN_WIDTH, scan_lines, 0, 0);
   }

}

void
Refresh_Screen()
{
   if (_IS_GRAPHICS_MODE_(Last_Mode) && _IS_HIRES_MODE_(Last_Mode))
   {
      Redraw_Hires_Screen(0, 159);
      if (_IS_MIXED_MODE_(Last_Mode))
	 Redraw_Screen(20, 23);
      else
	 Redraw_Hires_Screen(160, 191);
   }
   else
      Redraw_Screen(0, 23);

}

void
Redraw_Screen(start_row, end_row)
   int start_row, end_row;
{
   int i, j, x, y;

   ADDR row_address;

   BYTE *row_data;

   for (i = start_row, y = i * 16; i <= end_row; i++, y += 16)
   {
      row_address = Text_Lowres_Tag + line_base_offset[i];
      row_data = MainMemoryRAM[row_address >> 8] + (row_address & 0xff);

      for (j = 0, x = 0; j < 40; j++, x += 14)
	 XCopyArea(theDisplay,
		   text_lowres_line_fonts[i][*row_data++],
		   theWindow, theGC, 0, 0, 14, 16, x, y);
   }
}

void
Redraw_Hires_Screen(start_line, end_line)
   int start_line, end_line;
{

   if (_IS_PAGE_1_(Video_Mode))
   {
      if (Page1_Queue_Size)
	 Flush_Dirty_Queue(1, MAX_DIRTY_QUEUE_SIZE);
      XCopyArea(theDisplay, Hires_Page_1_Pixmap,
		theWindow, theGC, 0, start_line * 2,
		SCREEN_WIDTH, (end_line - start_line + 1) * 2,
		0, start_line * 2);
   }
   else
   {
      if (Page2_Queue_Size)
	 Flush_Dirty_Queue(2, MAX_DIRTY_QUEUE_SIZE);
      XCopyArea(theDisplay, Hires_Page_2_Pixmap,
		theWindow, theGC, 0, start_line * 2,
		SCREEN_WIDTH, (end_line - start_line + 1) * 2,
		0, start_line * 2);
   }

}

void
Write_Lowres_Video(addr, data)
   ADDR addr;

   BYTE data;
{
   register int linenum;     /* Searching for Y-coordinate */

   register int columnnum;   /* Searching for X-coordinate */

/*
 * if memory address not modified,
 * no screen update
 */

   if (MainMemoryRAM[addr >> 8][addr & 0xff] == data)
      return;
   else
      MainMemoryRAM[addr >> 8][addr & 0xff] = data;

   if (Text_Lowres_Tag == NO_DISPLAY)
      return;

/* Find which line the given address is in: */
   linenum = (addr >> 7) & 0x07;
   columnnum = addr & 0x7f;
   if (columnnum >= 120)
   /* If not on screen, don't draw anything: */
      return;
   else if (columnnum >= 80)
   {
      columnnum -= 80;
      linenum += 16;
   }
   else if (columnnum >= 40)
   {
      columnnum -= 40;
      linenum += 8;
   }

/*
 * check if text-lowres lines
 * are to be displayed
 */
   if (linenum < Text_Lowres_Top)
      return;

   Video_Dirty = 1;
/*
 * Copy character pixmap onto screen
 */
   XCopyArea(theDisplay, text_lowres_line_fonts[linenum][data],
	     theWindow, theGC, 0, 0, 14, 16, columnnum * 14, linenum * 16);

}

void
Write_Hires_Video(addr, data)
   ADDR addr;

   BYTE data;
{
   register int linenum;     /* Searching for Y-coordinate */

   register int columnnum;   /* Searching for X-coordinate */

/*
 * if memory address not modified,
 * no screen update
 */
   if (MainMemoryRAM[addr >> 8][addr & 0xff] == data)
      return;
   else
      MainMemoryRAM[addr >> 8][addr & 0xff] = data;

/* Find out the screen location of the address */
   linenum = ((addr >> 4) & 0x38) | ((addr >> 10) & 0x7);
   columnnum = addr & 0x7f;
   if (columnnum >= 120)
   /* If not on screen, don't draw anything */
      return;
   else if (columnnum >= 80)
   {
      columnnum -= 80;
      linenum += 128;
   }
   else if (columnnum >= 40)
   {
      columnnum -= 40;
      linenum += 64;
   }

/*
 * see if the byte is currently displayed
 * if so, update screen.
 */
   if (((addr & HIRES_MASK) == Hires_Tag) &&
       (linenum < (Text_Lowres_Top << 3)))
   {

   /*
    * Copy byte pixmap onto screen & buffer
    */

#ifdef COLOR
      if (columnnum % 2)
	 XCopyArea(theDisplay, hires_graphics_pixmap[data],
		 theWindow, theGC, 0, 0, 14, 2, columnnum * 14, linenum * 2);
      else
	 XCopyArea(theDisplay, hires_graphics_pixmap[data + 0x100],
		 theWindow, theGC, 0, 0, 14, 2, columnnum * 14, linenum * 2);
#else
      XCopyArea(theDisplay, hires_graphics_pixmap[data],
		theWindow, theGC, 0, 0, 14, 2, columnnum * 14, linenum * 2);
#endif

      Video_Dirty = 1;

   /*
    * backing store supporteb by X server, no need to update buffer pixmap
    * now.
    */
      if (HasBackingStore == Always)
	 return;
   }

/*
 * enqueue buffer pixmap update
 * since we have queues big enough for a whole
 * page, we don't need to check for queue overflow
 */
   if (addr < 0x4000)
   {				/* page 1 */
      if (!Page1_Dirty[addr - 0x2000])
      {				/* not yet in queue */
	 Page1_Dirty[addr - 0x2000] = 1;	/* mark address */
	 Page1_Address_Queue[MOD_MAX_QUEUE_SIZE(Page1_Queue_Head +
						Page1_Queue_Size)] = addr;
	 Page1_Queue_Size++;
      }
   }
   else
   {				/* page 2 */
      if (!Page2_Dirty[addr - 0x4000])
      {				/* not yet in queue */
	 Page2_Dirty[addr - 0x4000] = 1;	/* mark address */
	 Page2_Address_Queue[MOD_MAX_QUEUE_SIZE(Page2_Queue_Head +
						Page2_Queue_Size)] = addr;
	 Page2_Queue_Size++;
      }
   }

}

Flush_Dirty_Queue(page, count)
   int page, count;
{
   ADDR addr;

   int head, size;

   int linenum;		     /* Searching for Y-coordinate */

   int columnnum;	     /* Searching for X-coordinate */

   Pixmap buffer_pixmap;

   BYTE *dirty;

   ADDR *address_queue;

/*
 * move queue head & size to temp variables
 * and set the values of other local variables
 */
   if (page == 1)
   {
      head = Page1_Queue_Head;
      size = Page1_Queue_Size;
      address_queue = Page1_Address_Queue;
      buffer_pixmap = Hires_Page_1_Pixmap;
      dirty = Page1_Dirty - 0x2000;
   }
   else
   {
      head = Page2_Queue_Head;
      size = Page2_Queue_Size;
      address_queue = Page2_Address_Queue;
      buffer_pixmap = Hires_Page_2_Pixmap;
      dirty = Page2_Dirty - 0x4000;
   }

   if (!size)
      return;

   if (size < count)
      count = size;

   while (count-- > 0)
   {

   /* Find out the screen location of the address */
      addr = address_queue[head];
      linenum = ((addr >> 4) & 0x38) | ((addr >> 10) & 0x7);
      columnnum = addr & 0x7f;
      if (columnnum >= 80)
      {
	 columnnum -= 80;
	 linenum += 128;
      }
      else if (columnnum >= 40)
      {
	 columnnum -= 40;
	 linenum += 64;
      }

   /* Copy byte pixmap onto pixmap buffer */

#ifdef COLOR
      if (columnnum % 2)
	 XCopyArea(theDisplay,
		hires_graphics_pixmap[MainMemoryRAM[addr >> 8][addr & 0xff]],
	     buffer_pixmap, theGC, 0, 0, 14, 2, columnnum * 14, linenum * 2);
      else
	 XCopyArea(theDisplay,
	 hires_graphics_pixmap[MainMemoryRAM[addr >> 8][addr & 0xff] + 0x100],
	     buffer_pixmap, theGC, 0, 0, 14, 2, columnnum * 14, linenum * 2);
#else
      XCopyArea(theDisplay,
		hires_graphics_pixmap[MainMemoryRAM[addr >> 8][addr & 0xff]],
	     buffer_pixmap, theGC, 0, 0, 14, 2, columnnum * 14, linenum * 2);
#endif

   /* unmark address, note that dirty is offset by page base */
      dirty[addr] = 0;

   /* advance queue head & shrink queue */
      head = MOD_MAX_QUEUE_SIZE(head + 1);
      size--;
   }

/*
 * update queue head & size
 */
   if (page == 1)
   {
      Page1_Queue_Head = head;
      Page1_Queue_Size = size;
   }
   else
   {
      Page2_Queue_Head = head;
      Page2_Queue_Size = size;
   }

}