			      ================
			      = VGA Tutorial =
			      =      By      =
			      = Barny Mercer =
			      ================
			      Part I -  PIXELS
			      ----------------

Firstly, *many* thanks to Richard Griffiths for porting the code in this
tutorial to Pascal.

Hi and welcome to the first issue of my (hopefully) weekly tutorial on
programming graphics for the PC.

This tutorial deals with writing pixels to the screen and looks at a couple
of different methods of doing so.

Before we start :

-----------------------------------------------------------------------------
	DISCLAIMER

	The code for this tutorial, the compiled .EXEs and any ascociated text
	are freely distributable on the conditions that the contents of the 
	original .ZIP file are distributed as one and that no changes are made
	to any of the data or information contained within.
	The author expresses no warranty implied or otherwise as to the
	suitabilty of this software for execution on any machine other than
	the system on which it was developed (although there should be no
	problems).
	The author also takes no responsibilty for damage resulting from the
	use of information, code or otherwise, obtained from this tutorial 
	(again, I'd be surprised if such a thing did happen).
	Finally you should _NOT_ have paid anything for this tutorial.  
	If you parted with any money (other than a reasonalbe price for a 
	disk) then complain and get your money back.  Please let me know too. 
	This tutorial is FREE.  Please do not abuse this.
-----------------------------------------------------------------------------

Sorry about all that, but unfortunately we live in a world where such things
are a necessity.

Right!

There are a number video modes available on the VGA graphics card.  Each one
is identified by a value which is passes to the BIOS interrupt 10h 
required to call it.  If this is nonsense to you then don't worry.  
All will become clear...... (I hope)

During this tutorial we will be concentrating on one particular video mode.

Mode 13h
--------

Mode 13h is 320 pixels wide by 200 pixels and capable of displaying 256 
colours on screen at one time. We therefore have 64000 pixels to play with. 

Each pixel on screen is represented by 1 byte of video memory.  Graphics are
basically created by setting the value of these bytes.  A byte can store
values in the range of 0 to 255, that's 256 different values available.
Each one of these values is linked to a colour index which informs the VGA
card which colour is wanted on screen.

Easy?  Absolutely.

--------------------------
So how do I place a pixel?

In order to demonstrate we'll write a short program to select the appropriate
video mode, place some pixels and return to text mode.

That specification is already nicely broken into simple pieces so we will
expand on each one.

	Select mode 13h
	---------------

	The easiest and quickest way to do this is with assembly, but I will
	explain the principle involved first.

	The BIOS interrupt number for changing the VGA mode is 10h (that's 
	16 decimal, for anyone who's unfamiliar with hexadecimal).
	There are lots of other interrupt numbers for dealing with the VGA 
	but we will concentrate on 10h for now.

	To switch to mode 13h we need to tell the ROM BIOS two things :
	
	1. Which mode we want (13h in this case)
	2. And what to do with that number (A video operation)

	So the code looks like this :

	// -----------------------------------
	// Mode 13h Selection - Barny Mercer
	// -----------------------------------

	void main(void)
	{
		_asm    // tell the compiler that we are writing assembly code
		{
			mov ax, 13h      // store 13h in AX
			int 10h          // call interrupt
		}
	}

	Note the use of the AX register.  The INT instruction passes the value
	stored in AX to the VGA card and this is what sets the video mode.

	By placing different values in AX we can access different video modes.

	If you are not familliar with assembly, then do not worry.  You can 
	still use the code, and learn as you go along.  If you need a quick 
	assembly tutorial then drop me a line.

	That's it!  Erm... well not quite.  This program will switch to
	mode 13h but leave you there when it termiates.  We need a way of
	returning to text mode when we are done.  Easy...!

	To return to text mode we just replace 13h in the above code with
	the appropriate video mode.  25 line text mode has the value 03h, so..

	// -----------------------------------
	// Text Mode Selection - Barny Mercer
	// -----------------------------------

	void main(void)
	{
		_asm
		{
			mov ax, 03h      // store 03h in AX
			int 10h          // call interrupt
		}
	}

	There...  2 programs for switching video modes.

	'Errmmm...', I hear you say, 'They're not really very useful are
	they?!'.  Well, no, but they illustrate what we need to know.

	To make them more useful we will turn them into a single function
	which can be called whenever you need to switch video modes.

	Take a look at this code:

	// ----------------------------------------
	// Mode Selection Function - Barny Mercer
	// ----------------------------------------

	// define preprocessor constants
	#define VID_320x200 0x0013              // 0x informs the compiler
	#define VID_TEXT    0x0003              // that the value is in hex

	void SetVideo( int Mode )
	{
		_asm
		{
			mov ax, [Mode]   // store parameter in AX
			int 10h          // call interrupt
		}
	}

	Now when you want to switch video modes in your programs you do so
	by calling the function as follows:

	SetVideo( VID_320x200 );                // select 320x200x256 - 13h
	or
	SetVideo( VID_TEXT );                   // select text mode

	Easy peasy!  (cheesy expression number 1.)

Well... we've managed to cover 2 of the three steps that we outlined above, so
without further ado, let's move on to the most interesting bit.

	Placing Pixels
	--------------

	There are essentially two ways that this can be done.

		Interrupts

			By using a smilar technique to the one above we can
			send a pixel to video memory via the ROM BIOS.
			Using BIOS however is a very slow way of doing things.

		Direct Memory Access (DMA)

			A faster method is to avoid the BIOS completely and
			write our value directly to memory.

	I'll talk first about the DMA method.

	In order to place a pixel we need to know two things.  The location 
	of the pixel and the colour.  Obvious?  Of course it is.

	Now comes the tricky bit.  When you look at your monitor what do you
	see?  200 rows of 320 pixels.  Fair enough, but that is not the way
	in which the screen is represented as data.

	Video memory is, for most purposes, treated just like ordinary memory.
	A specific point in memory is loacted by two values. Both of these 
	are 16 bit values (range 0 - 65535).  The first (SEGMENT) points to 
	a 64k chunk of memory and the second (OFFSET) points to a specific 
	byte inside that chunk.  Video memory for the VGA has the segment 
	0xA000.  Therefore we say that the address of the first byte of video
	memory is A000:0000 with the next byte appearing at (logically) 
	A000:0001.  Remember that these addresses are in hexadecimal.

	(A hexadecimal/binary/decemal explanation is not provided here, but
	 if you need one then feel free to contact me and I'll be happy to
	 oblige)

	The video memory offset therefore extends from 0000 (0) to 
	FFFF (65535) (although only the first FA00 (64000 = 320x200) are
	visible.  

	To place a pixel on screen, we stuff the appropriate value in the 
	appropriate memory location!  Hoorah!! <wave banners> <sing songs>
	<generally celebrate> <gradually realise that there must be a catch>
	<stop singing> <wait for bad news>

	While your image occurs on a 320x200 grid using a coordinate system, 
	we are actually placing values in memory by specifying only 1 value.
	By modifying the offset we can point to a specific byte.
	Actually, it's not so difficult.  A very simple formula allows us to 
	locate the correct offset based on our (X, Y) values.

	offset = (Y * 320) + x

	Therefore, a point (55,100) has the offset
	( ( 100 * 320 ) + 55 ) = 32055 (0x7D37)
	
	To place a white pixel at point (55,100) we would place the value
	0x0F (15 dec) at byte 0x7D37.  The address looks like this A000:7D37

	All okay so far?  Good.  I'm glad to hear it.  What?!  It's not?
	Ok, e-mail me for more details on memory too then.

Ok.  Now, we know how to find the correct memory location.  How do we
actually place a value there?  I'm so glad you asked that question,
because it shows that you are paying attention.  So, moving steadily
onward........

	PLACING A PIXEL!
	----------------

	As I mentioned above there are basically two ways to do this.  I will
	code the first and explain it as I go along.  It is simpler because
	we don't have to calculate the offset or fiddle with memory.

	Here we go.....

	// -------------------------------------
	// PutIntPixel Function - Barny Mercer
	// -------------------------------------

	void PutIntPixel( int X, int Y, int Colour )
	{
		_asm
		{
			mov ah,  0x0C       ; specify apropriate function
			mov al,  [Colour]   ; put required colour in low byte
			mov cx,       [X]   ; X coordinate
			mov dx,       [Y]   ; Y coordinate
			mov bx, 0x01        
			int 10h             ; execute interrupt
		}
	}

	I've demonstrated this function in Intpixel.exe (source also included)

	The biggest drawback to this function is that it is soooo _s_l_o_w_
	So now I will demonstrate the DMA principles that we examined earlier.

	Oh..!  One extra thing before I begin coding.
	In order to access a specific memory segment we need to be able to
	point to it in some way.  The way we do this is by using a pointer
	to the correct segment. Like this....

	unsigned char *vga = (unsigned char *)0xA0000000;        

	Now the variable 'vga' can be used to reference our video memory.

	// -------------------------------------
	// PutDMAPixel Function - Barny Mercer
	// -------------------------------------

	void PutDMAPixel( int X, int Y, int Colour )
	{
		unsigned int Offset;

		Offset = (Y*320)+X;             // this could be done
						   on-the-fly but I have used
						   a variable for clarity

		// memset(vga+(Y*320)+X, Colour, 1);  // on-the-fly version

		memset(vga+Offset, Colour, 1);  // note that you could place
						   many pixels by replacing
						   the 1
	}

	Ok.  This is demonstrated in 'dmapixel.exe' (source also included).

	It's still not very fast though.  
	'What can we do to make it faster?' I hear you murmur through your
	boredom induced haze, uncertain as to weather asking the question was
	wise.

	I am afraid that in order to speed things up, we're going to have to
	do a bit more assembly.  All set?  No?  Tough.....

	First, I will code the above routine in assembler and then we can
	look at making it faster.  Ha-wun, Ha-two, Ha-wun, two, three, four..
	Ahem...

	// -------------------------------------
	// PutASMPixel Function - Barny Mercer
	// -------------------------------------

	void PutASMPixel( int X, int Y, int Colour )
	{
		_asm
		{
			mov ax, 0xA000   ; point AX to video memory
			mov es, ax       ; move segment pointer to ES
					   (actual pointer)

			; calculate offset
			mov ax, [Y]      ; place Y value in AX
			mul ax, 320      ; AX=Y*320
			add ax, [X]      ; add X coordinate to AX
					 ; now AX = (Y*320)+X, as per previous
					 ; example

			; move offset to offset pointer
			mov di, ax

			mov es:[di], [Colour] ; move colour to memory
		}
	}

	Ok.  Looks complicated, but it isn't that bad really.  It is basically
	the same as the previous one but written in assembly so that we can
	see what needs doing to speed it up a bit.

	The first thing that I notice is the use of the MUL command.
	A MUL is _very_ _very_ slow and this is what is slowing this
	function down.  Sooooo.... let's remove it!
	
	'Please Sir.  How do we remove it and still find the correct offset?'
	chirrups a small voice from the back of the class.

	If Y = 104 then Y*320 = 33280

	104 * 256 = 26624
	104 *  64 =  6656
	      ---   -----
	      320   33280

	'What advantage is there to doing it in two stages?, you may well ask.
	And that I will endevour to make clear. Take a look....

	SHL register, [value]

	The assembly SHL (SHift logical Left) command rotates a 16 bit value
	stored in the register passed by the number of binary places in 
	'value'.

	Looking at binary patters we can see the following:
	
	104 x 320 = 33280

	01101000 * 0000000101000000 = 0110100000000000  (26624)
				    + 0001101000000000  ( 6656)
				      ----------------
				    = 1000001000000000 = 33280
				      ----------------

	So by doing SHL ax, 8           ; ax = Y * 256
		    SHL bx, 6           ; bx = Y *  64
		    ADD ax, bx          ; ax = (Y*256)+(Y*64) = (Y*320)
	
	Instead of  MUL ax, 320         ; ax = (Y*320)
	what have we gained?

	Take a look at the following table:

	Operation  Clocks (486)    1 clock = (1000/CPU speed Mhz) nanoseconds

	ADD        1
	MOV        1
	SHL        3
	MUL        26!

	So as you can see, method 1 requires only 7 clocks to execute while
	the MUL method requires 26 clock ticks.

	And there you have it!

	Oh....  There is a way to shave another 2 clocks off that multiply
	routine.  Instead of doing (SHL ax, 8), a (MOV ah, al) will achieve
	the same effect but 2 clocks faster.

	Here is the finished PutPixel function.

	void PutASMPixel( int X, int Y, unsigned char Colour )
	{
		_asm
		{
			mov ax, 0xA000   ; point AX to video memory
			mov es, ax       ; move segment pointer to ES
					 ; (actual pointer)
			mov bx, [Y]
			mov ax, bx       ; register to register is faster by 1 clock

			mov ah, al       ; ax=y*256 + y
			mov al,  0       ; ax=y*256

			shl bx, 6        ; bx=y*64
			add bx, ax       ; bx=y*320

			add bx, [X]      ; ax=(y*320)+x
			mov di, bx       ; move video pointer to correct place

			mov al, [Colour]
			mov es:[di], al  ; move colour to memory
		}
	}

	And there you go!  A nice fast assembly PutPixel routine.

	A lot of work?  Well yes, but only because of the need for speed in
	a pixel routine.  A slow PutPixel will result in other routines which
	require it to be slowed too.

	It is possible to create a faster PutPixel, but I'll leave you with
	this one for now.

I hope that this tutorial has been helpful to you.  It is by no means perfect,
but that doesn't make it worthless.

If there is anything in this tutorial which you would like further details on,
then please do not hesitate to contact me.

If you produce anything nice, then please send it to me.  I am always keen
to see other people's work.

Comments on this tutorial will be gratefully recieved.  Was it any use to you?
Too informal?  Too complicated?  Not clear enough?  Too simple?
What subjects would you like to see covered here?

-------------------------------------------------------------------------------

'The true measure of a hero, is one who can lay down his life for others
 in the knowledge that no one will ever know......'

 (Source unknown)
------------------------------------------------------------------------------

Barny Mercer    - (original code & text file) 29/7/95 @ 12:22am

email : barny.mercer@zetnet.co.uk
WWW   : http://zetnet.co.uk/users/bmercer/
voice : 01595 692097 (UK)

Richard Griffiths       - Pascal conversion
email : richard.griffiths@zetnet.co.uk
WWW   : http://zetnet.co.uk/users/rgriff/
	
