//-------------------------------------------------------------------
//	michener.cpp
//
//	This program uses the Michener Algorithm to fill a circle.
//
//	 compile using:  $ g++ michener.cpp int86.cpp -o michener
//
//	programmer: ALLAN CRUSE
//	written on: 20 SEP 2003
//	revised on: 27 SEP 2005 -- replaces svgalib with 'int86()'
//-------------------------------------------------------------------

#include <stdio.h>	// for printf(), perror() 
#include <fcntl.h>	// for open() 
#include <stdlib.h>	// for exit() 
#include <unistd.h>	// for close() 
#include <sys/mman.h>	// for mman() 
#include "int86.h"	// for init8086(), int86()

unsigned char 	*vram = (unsigned char*)0xA0000000;
int	vesa_mode = 0x4103, hres = 800, vres = 600; 
struct vm86plus_struct	vm;

void draw_rectangle( int x, int y, int h, int v, int color )
{
	int	minx = x, miny = y, maxx = x+h-1, maxy = y+v-1;

	do { vram[ y*hres + x ] = color; ++x; } while ( x < maxx );
	do { vram[ y*hres + x ] = color; ++y; } while ( y < maxy );
	do { vram[ y*hres + x ] = color; --x; } while ( x > minx );
	do { vram[ y*hres + x ] = color; --y; } while ( y > miny );
}

void draw_segments( int x, int y, int xcent, int ycent, int color )
{	
	int	minx = 0, miny = 0, maxx = hres-1, maxy = vres-1;
	int	xlo, xhi, v, h;

	xlo = xcent-x, xhi = xcent+x;
	if ( xlo < minx ) xlo = minx;
	if ( xhi > maxx ) xhi = maxx;
	
	if ( ( v = ycent-y ) >= miny )
		for (h = xlo; h <= xhi; h++) vram[ v*hres + h ] = color;	
	if ( ( v = ycent+y ) <= maxy )
		for (h = xlo; h <= xhi; h++) vram[ v*hres + h ] = color;	
	
	xlo = xcent-y, xhi = xcent+y;
	if ( xlo < minx ) xlo = minx;
	if ( xhi > maxx ) xhi = maxx;
	
	if ( ( v = ycent-x ) >= miny )
		for (h = xlo; h <= xhi; h++) vram[ v*hres + h ] = color;	
	if ( ( v = ycent+x ) <= maxy )
		for (h = xlo; h <= xhi; h++) vram[ v*hres + h ] = color;	
}


void fill_circle( int x, int y, int radius, int color )
{
	int	xcent = x, ycent = y, drop = 3 - 2*radius;
	y = radius, x = 0;
	while ( x <= y )
		{
		draw_segments( x, y, xcent, ycent, color );
		if ( drop < 0 ) { drop += 4*x + 6; }
		else 	{
			drop += 4*(x - y) + 10;
			y -= 1;
			}
		x += 1;
		}	
}


int main( int argc, char **argv )
{
	// memory-map VRAM to this application's address-space
	int	fd = open( "/dev/vram", O_RDWR ); 
	if ( fd < 0 ) { perror( "/dev/vram" ); exit(1); } 
	int	size = lseek( fd, 0, SEEK_END );
	int	prot = PROT_READ | PROT_WRITE; 
	int	flag = MAP_FIXED | MAP_SHARED; 
	void	*mm = (void*)vram;
	if ( mmap( mm, size, prot, flag, fd, 0 ) == MAP_FAILED ) 
		{ perror( "mmap" ); exit(1); } 

	// enter graphics mode
	init8086();
	vm.regs.eax = 0x4F02;		// VESA Set Display Mode
	vm.regs.ebx = vesa_mode;	// the VESA mode-number
	int86( 0x10, vm );		// invoke BIOS service
	
	// draw screen border
	int	white = 15, magenta = 5;
	draw_rectangle( 0, 0, hres, vres, white );
	getchar();
	
	// use Michener algorithm to fill a circular disk
	int	midx = hres/2, midy = vres/2, radius = vres/4;
	fill_circle( midx, midy, radius, magenta );
	getchar();

	// leave graphics mode
	vm.regs.eax = 0x0003;		// VGA Set Display Mode 3
	int86( 0x10, vm );		// invoke BIOS service
}