Site hosted by Angelfire.com: Build your free website today!



Overview.

C Tutorials

C/C++ Code/Examples.

PostScript Tutorials.

Visit Crisp Design Central.

Mail Web Master




Tutorials

Here are the tutorials we have written so far:

Pointers

Rebooting

Bitwise Operators

Changing Colours with Outport

Dos File Arguments

Calling Interupts

Arrays

Drawing Lines


Using Pointers

Pointers are variables that 'point' to an area of memory. They are one of the hardest parts of C to understand, but they are also very useful.

Note: in C, a character is surrounded by apostrophes, eg, 'h' and a string is surrounded by inverted commas, eg, "hello my name is".

To declare pointers, you put an * in front of the variable name. eg: char* p. This declares a character pointer to a random byte of memory. Now, if we make an array:

char a[20];

and point p at it:

p = a;

p is now pointing to the adress in memory of the first character of a. Now, say a contains "xyz", p is pointing to 'x'. To acces the char that p is pointing to you use the * operator, ie *p is 'x'. Now, say we want to change the character that p is pointing to to 'f' we say *p='f'. Now a holds the string "fyz". Say you want to change the second letter in the array to 'g', you can say *(p+1)='g' or you can actually move where p is pointing to by 1. ie,

p=p+1; //move adress of p 1 char further forward in memory

*p='g'; //set char at that adress to 'g'

The boring theory bit out of the way, why would you want to a pointer?

Well, say you wanted to plot pixels on the screen. Then you point a pointer to video memory and write into it (for more details, download the example: Pixel Plotting in 256 Colour VGA in the examples section). Pretty powerful stuff!

Something a bit more basic: if you wanted to change every occurence of the letter 'a' in a string to a capital 'A' then you could do it nicely with pointers.

char a[20]; //make array of chars 20 long

char* p=a; //make a pointer to array

srtcpy(a, "alex met alexandra"); //copy string "alex met alexandra" into array a

while ( *p!=0 ) //remember that the last character in an array is a 0, so this will loop until p points to the end of the string

{

    if ( *p=='a' ) //if p is pointing to a little 'a'

      *p='A'; //replace it with a big 'A'

    p++; //move where p is pointing to forward by one char

}

so, array a will now hold "Alex met AlexandrA".

This is a very efficient way of carrying out such an operation.

        - By James Crisp


Drawing Lines

Why write a function to draw lines
If you are using mode 13h, and you are plotting pixels straight to memory, then you cannot use your normal graphic functions, that enable you to draw lines. You have to draw them yourself. This is why the following functions were written. Note that some of them refer to plot_pixel(int x, int y, char c). This is just your pixel plotting function.

How to draw lines
There are a few different ways to draw lines. You can make certain assumptions about the lines that you are going to draw, if you are writting them for some of your programs.

Horizontal lines
For example, if you know that you are going to get lines, that are always horizontal then you can write a simple function like this:

void simple_hoz_line(int x1, int x2, int y, char c){
      int counter;
      char far *vid_pointer = (char far*)MK_FP(0xA000, y * 320 + x1);

      for(counter = x1; counter <= x2; counter++, vid_pointer++)
            *vid_pointer=c;
}

The above code, could be used to draw horizontal lines, between two points x1, and x2. However there are some limitations, x1 must be smaller than x2. Also the line, is not clipped against the edges of the screen. If you indicate, a pixel such as (500, 134), it would actually output to (180, 135). This is because, there would be an overflow. You could add checks to the line drawing mechanism, but it would slow it down. The things that you would be looking for, are things such as boundary checking (is all the line on the screen, or even worse, is the line on the screen at all) and co-ordinate switching (if x1 is greater than x2, you might want to swap them around).

Vertical lines
Slightly harder to implement, is the vertical line. This time we can't simply add 1 to the location, as we also need to change the row its on. This can be done by adding 320 to the location.

void simple_v_line(int x, int y1, int y2){
      int counter;
      char far *vid_pointer = (char far*)MK_FP(0xA000, y * 320 + x1);

      for(counter = y1; counter <=y2; counter++, vid_pointer+=320)
            *vid_pointer=c;
}


Other lines
Now, we come to the more complicated part. Those two functions, are fine, if you just want to draw simple lines. If however, you want to have lines, that are not vertical or horizontal then you will need to use longer, slower, and larger functions. The lines you draw in this manner, will not as accurate, as the previous ones. This time, you will only draw groups of pixels, that will approximate a line.

There are two ways of drawing a line. You can use the equation of the line, or the Bresenham's algorithm. The easiest to implement (and the only one I will talk about for now) , is equation of the line way. This is done, by taking the equation in the form y=mx+b . If you forgot all your co-ordinate geometry, this is what it means: b is the y-intercept, ie where the line crosses the y-axis. x, is the x co-ordinate and m is the gradient. This is given by the rise divided by the run: (y2 - y1) / (x2 - x1) . If we want the line to look smooth, we must have two posibilities: the gradient is greater than 1, or the gradient is smaller than one. If the gradient is greater than 1, then we need to find what the x co-ordinate is for each y co-ordinate. If we did the opposite, there would be some gaps in the line. If however the gradient is smaller than 1, then we need to find each y co-ordinate, for each x co-ordinate. Also, when you are finding out the gradient, if the two x co-ordinates are equal, then you will get a division by 0. That is why it is the first thing that is done in my example. If you haven't understood any of what I just said, just look at this:
void complex_line(int x1, int y1, int x2, int y2, char c){
      int counter;
      double temp;
      double signed m;

      if (x2 == x1)
            for (counter = y1; counter <= y2; counter++)
                  plot_pixel(x1, counter, c);
      m = (double)(y2 - y1) / (double)(x2 - x1);
      if (m < 1)
            for (counter = x1; counter <= x2; counter++){
                  temp = m * counter - m * x1 + y1;
                  plot_pixel(counter, (int)temp, c);
            }
      else
            for (counter = y1; counter <= y2; counter++){
                temp = (counter - y1 + (m * x1)) / m;
                plot_pixel((int)temp, counter, c);
            }
}

        - By Pascal Hakim


Rebooting the Computer

#define BLAA 0 /* for cold restart */

/* #define BLAA 0x1234 /* for warm restart */

#define BOOT_SEG 0xffffL

#define BOOT_OFF 0x0000L

#define BOOT_ADR ((BOOT_SEG << 16) | BOOT_OFF)

#define DOS_SEG 0x0040L

#define RESET_FLAG 0x0072L

#define RESET_ADR ((DOS_SEG << 16) | RESET_FLAG)

int main()

{

void ((far *fp)()) = (void (far *)()) BOOT_ADR;

*(int far *)RESET_ADR = BLAA;

(*fp)();

return 0; //s'pose this is not really needed!

}


Bit-Wise Operators

Bit-wise operators operate on strings of bits. As you know, binary code is just lots of bits, zeroes and ones.

A "char" has 8 bits = 1 byte.

A "short int" has 16 bits = 2 bytes.

A "long int" has 32 bits = 4 bytes.

lets just work with char's at the moment.

Operator: << and >>

Say you have c=0110 0110 (i'll split the numbers up into lots of 4 so its easier to read). If you do c>>1 that means shift all bits in c to the right by 1. So c=c>>1 makes c==0011 0011. The >> operator just adds a blank zero on where there would have been an empty space. A char is always 8 bits so if we shift everything to the right by one, we have to fill the space left over on the left.

Similarly, << makes the bits shift left. so if c=01100110 then c<<1 == 11001100 (it adds a zero on at the right).

Why would you want to do this, you ask? Well, take normal base 10 numbers.

If you say shift the digits left in 0123 you get 1230 ... notice something?

Yes, its been multiplied by 10. Similarly, if you do >> then its divided by ten.

So with binary, which is base 2, a << multiplies by 2 and >> divides by 2. All without having to worry about division and remainder and all that. It really comes in handy when you are thinking about things in terms of 2, for instance when I need to divide by 1024 I just use NUM>>10 since 1024 equals 2 to the power of 10.

In the old days, assembler programmers used << and >> wherever possible since they were many many times faster than using a division. But that doesn't really matter now in this age of pentiums... except maybe the stupid computers at school :)

Operator: | & ^ !

Well, these operate on bits as well.

| = OR & = AND ^ = Exclusive OR ! = COMPLEMENT

Lets write it out like sums in first form, so we can see just whats going on:

00101100 |

10100100

----------

= 10101100

Here, in the answer there is a ONE in the position where there is a ONE in the first OR second OR both numbers.

00101100 &

10100100

----------

= 00100100

Here, in the answer there is a ONE in the position where there is a ONE in the first AND the second number.

00101100 ^

10100100

----------

= 10001000 Here, in the answer there is a ONE in the position where there is a ONE in the first OR second but NOT both. (hence the term "exclusive OR")

! 00101100 [The complement operator only takes one number, not two.]

----------

= 11010011

Here, in the answer there is a ONE in the position where there was a ZERO in the original number, and a ZERO where there was a ONE in the original number, ie. it flipped all the 1's to 0's and vice-versa. Here are some worked examples. Say you are given a variable "flags" and it has some bits set. You want to check if the second bit (from the right) is set. Now, a number with only the second bit set is 00000010 which = 0x02 in hex. So we AND it:

if(flags&0x02)

{ do stuff... }

Because, say for example, flags=01110110. Then 01110110 & 00000010 = 00000010 which equals 2. because this is not zero, the if statement is true. *** remember, in C, if something is not zero it is 'true' ***

Say flags was equal to 01110100 (ie. the second bit was NOT set). Then 01110100 & 00000010 = 00000000 which is zero. i.e. the if statement would have been false, and the { do stuff } section would not have been executed.

Get it ?!?! :)

- By Andrew Snow