Sie sind auf Seite 1von 42

Welcome to Advanced Concepts in C

Prerequisite:
To understand these new concepts in C, the basic knowledge of pointers in C and Internal Architecture of Microprocessor is required.

Internal Architecture of Microprocessor

General-Purpose Registers

EU(Execution Unit of Microprocessor) has Eight 8-bit generalpurpose registers, AH, AL BH, BL CH, CL DH and DL These registers can be used individually for temporary storage of 8-bit data. Register pairs AH-AL, BH-BL, CH-CL, and DH-DL can be used together to form register AX, BX, CX, and DX and can be used to store 16-bit data words. The AL register is also called the Accumulator.

Segment Registers The 8086 microprocessor has Four Segment Registers: CS (Code Segment Register) DS (Data Segment Register) SS (Stack Segment Register) ES (Extra Segment Register) These registers are used to refer Segment part of Memory address. The 8086 microprocessor has Five Offset Registers: IP (Instruction Pointer or Program Counter) SP (Stack Pointer) BP (Base Pointer) SI (Source Index) DI (Destination Index) These registers are used to refer Offset part of Memory address.

Types of Pointers
Near Pointers Far Pointers Huge Pointers
Near Pointers: A Near Pointer is of 16-bits. It uses the current contents of the CS(Code Segment) register or DS(Data Segment) register for Segment part Whereas the offset part is stored in 16-bits NEAR pointer. Clearly this type of Pointer limits us to 64KB memory of a particular segment.

Far Pointers: A Far Pointer is of 32-bits long. This type of Pointer contains both segment and offset. By using it we can point to multiple segments so Limit of 216 = 26 X 210 = 64kb does not exist for Far Pointers Hence with Far Pointers we can address more than 64kb of data.

Huge Pointers: A Huge Pointer is of 32-bits long. This is same as Far pointer except one difference that here Segment: Offset address is always Normalized. So the offset always contains value from 0 to F For Example : an Address 500D : 9407 will be converted to 594D : 0007 as shown below:

Segment: 500D0 (0 is appended automatically ) + 9407 After Addition we get 594D7 Which is Normalized as 594D : 0007

Pointers and Typecasting


We know that an Integer Pointer contain the address of an Integer. Similarly a Float pointer contain the address of an Float and so on But if we try to assign address of a Float to an Integer Pointer and Vice Versa then we get a typical Warning i.e. Suspicious Pointer Conversion Consider the program given below: main() /* SPC.C */ { int i=10; float *p; p=&i; /* Suspicious Pointer Conversion */ }

The problem of Suspicious Pointer Conversion can be solved by using Type Casting as shown in the program given below: main() /* TYPEc.C */ { int i=10; float *p; p=(float *)&i; }

Now let us see another program which tries to refer the address which lies outside the DS(Data Segment) of our program.For achieving this we have to use Far Pointer as Given below:

main()
{

/* SCR.C */

char far *screen; screen = (char far *)0xB8000000L; *screen = A; }

How it Works ?
The address 0xB8000000L is a an address of the beginning of Colored VDU Memory Block. For Monochrome Adapter the address is 0xB0000000L. Each Character present on screen has TWO bytes corresponding to it in the VDU memory. The FIRST byte contains the ASCII Value of the character. The SECOND byte contains COLOR of that character. So if A is displayed on row -0 and column -0 on the screen then address 0xB8000000L contains ASCII value of A and address (0xB8000000L + 1) contains the COLOR of A Hence if One character requires TWO Bytes the One Screenful of characters will require 80 * 25 * 2 = 4000 Bytes .

Consider the program given below:


main()

/* SCRofA.C */

{
char far *screen = (char far *)0xB8000000L; int i; for ( i = 0 ; i <= 3999 ; i = i + 2) *(screen + i ) = A; } Note : What will happen if variable I is incremented one at a time ?

Interrupts and Interrupt Vector Table


An Interrupt is a Signal to the Microprocessor to Stop current task and execute some other task. An Interrupt may be generated through Software by using int86() function in C or through Hardware e.g. When we Hit a key from the keyboard , a Hardware Interrupt is generated. When an Interrupt occurs, the Microprocessor stops its current processing activity and executes an Interrupt Service Routine(ISR) corresponding to the interrupt generated. The address of the ISR is stored in Interrupt Vector Table(IVT) The moment Microprocessor finds Address from IVT, it does stores current contents of various registers on the Stack. The control is then transferred to the ISR to be executed. Once the ISR has been executed, the Microprocessor returns back to its previous activity.

Interrupt No. Decimal Hexadecimal 16 10 19 13 20 14 22 23 18 26 16 17 12 1A

Purpose Video Display Diskette Services Communication Devices Keyboard Services Printer Services Memory size Time and Date

Program to show contents of IVT in Memory


/* IVT.C */ #include "stdio.h" #include "dos.h" main() { unsigned long far *address = (unsigned long far *)0x00000000; unsigned long intadd[256]; unsigned int segment,offset; int i; FILE *fp; fp = fopen("IVT.TXT","wb"); for(i=0 ; i<256 ;i++) { intadd[i] = *(address++); segment = FP_SEG(intadd[i]); offset = FP_OFF(intadd[i]); fprintf(fp,"Interrupt %3X : Vector %Fp(hex) : %lu(dec)\r\n",i,intadd[i],(unsigned long)segment*16+offset); } fclose(fp); }

int86() Function
This function is used to make a Software Interrupt. Here int stands for Interrupt and 86 stands for 8086 family of Microprocessors. It has following Three arguments: Interrupt Number Two union variables

The First union variable refers to values passed and Second for values returned by the int86() function.
e.g. int86(16, &inregs, &outregs); Where

16 is the Interrupt Number


inregs represents register values sent to it outregs represents register values sent by it

Using int86() Function to find Memory Size


Consider the program given below:

/* MEMsize.C */
#include dos.h /* union variables are declared in it */ main() { union REGS inregs, outregs; int memsize; int86(18, &inregs, &outregs); memsize = outregs.x.ax; printf(\n Total Memory (RAM) = %d,memsize); }

In this program we have not passed any values to the inregs because the Interrupt Number 18 does not require it, but it returns memory size in the register ax whose contents can be accessed by using outregs.x.ax, Where x is a variable of union variable REGS.

Let us see another program to print a TEXT at any Row and Column on the Screen..

/* Textrc.C */
#include dos.h /* union variables are declared in it */ main() { union REGS inregs, outregs; inregs.h.ah = 2; /* Service Number */ inregs.h.dh = 10; /* Row Number */ inregs.h.dl = 2; /* Column number */ int86(16, &inregs, &outregs); printf(\n Hello);

In this program, We have passed some values to the function int86(). Upon return from the function, the cursor is set to the position mentioned in the program. Hence depending upon the Service Number of the Interrupt, we have to pass and receive the values through inregs and outregs variables.

Pointers to Functions

As we know that Pointers can be used as Reference Variables to any of the Data types in C. Similarly Pointers can also be used to point C Functions. So if we know the functions address, we can point to it. In C, We can find the Address of any function. This Address then can be assigned to a Pointer.

Let us see the following program to Find Address of a function: main( ) /* p_fun.c */ { int display( ); printf ( \n Address of function display is %u, display); display( ); } display( ) { printf( \n In function display ); }

So from above program it is clear that we can find the Address of any function. Let us see following program in which Address of Function is assigned to a Pointer as given below: main( ) { int display( ); int (*fp)(); /* Declaration of Pointer to a Function */ fp = display; /* Assigning Address of Function to Pointer */ printf ( \n Address of function display is %u, display); (*fp)(); /* Invoking Function through Pointer Fp*/ } display( ) { printf( \n In function display ); } /*

p_fun2.c */

So from above program it is clear that we can call any function with the help of Pointers.. Let us see following program in which Address of a number of Functions is assigned to an array of Pointers as given below:

main( ) {

/* p_fun3.c */ int i,fun1( ), fun2(), fun3(); int (*fp[3])(); /* Declaration of Pointer Array to a Function */ fp[0] = fun1; /* Assigning Address of Function to Pointer */ fp[1] = fun2; fp[2] = fun1; for( i=0 ; i <=2 ; i++) (*fp[i])(); /* Invoking Function through Pointer Array Fp*/

} fun1( ) { printf( \n In function One ); } fun2( ) { printf( \n In function Two ); } fun3( ) { printf( \n In function Three ); }

So from above program it is clear that we can call any number of functions with the help of Pointers, which is an advantage of using Pointers. Let us see following program in which a ROM-BIOS (Read Only Memory Basic Input Output system)Function used to Exit from DOS, which resides at memory location 0xFFFF0000:

#include dos.h

/* ExitFrom.c */

main( ) { void far (*p)( ); p = 0xFFFF0000; (*p)(); /* Invoking ROM BIOS Function through Pointer p */ }

Introduction of TSR

Introduction of TSR

TSR stands for:

Terminateand Stay-Resident programs

Whats Special about TSR


Terminate-and-Stay-Resident (TSR) are programs which get loaded in memory and remain or stay there (resident) in memory permanently. They will be removed only when the computer is Rebooted or if the TSR is explicitly removed from memory. Until then they will stay (resident) in memory active.

Working Technology of TSR


When TSR is not running it does not affect the running of other DOS programs. It stays in memory in idle state. Only thing it does is it occupies memory space and the occupied TSR memory space cannot be occupied by other programs. The TSR which is now in memory is invoked only when some special keys are pressed.

The First TSR


Let us try to Change the contents of IVT such that the location number 36 to 39 contain the Address of OUR function , instead of the normal ROM-BIOS routine. We will also make the program resident in memory even when its execution is terminated.

Let us see following program in which a TSR changes characters on screen as given below: /* TSR1.c */ #include dos.h void interrupt our(); void interrupt (*prev)(); char far *scr = (char far *)0xB8000000L; main( ) { unsigned long int far *p; p = (char far *)36; /* Declaration of Pointer to Location 36 of IVT */ prev = *p; /* Save existing Address of ROM-BIOS Function to Pointer */ *p = our; /* Set up New Address */ keep(0,500); /* Make program resident in Memory */ }

/* TSR1

.c

Continued */

void interrupt our( ) { int i; for ( i = 0 ; i<= 3999 ; i = i + 2 ) { if ( *(scr + i) >= A && *(scr + i) <= Z ) *(scr + i) += 32; /* Change Uppercase to Lowercase */ if ( *(scr + i) >= a && *(scr + i) <= z ) *(scr + i) - = 32; /* Change Lowercase to Uppercase */ } (*prev)(); }

Explanation of The First TSR


Variable p is declared unsigned long int pointer because the address stored at location 36 to 39 is 4

bytes long.

Keep() function is used to request DOS to allocate 500 * 16 Bytes to OUR program. It will make our Program Resident as this memory will never be given to any other program running under DOS. What happens actually when We hit a key from the keyboard Firstly, Interrupt Number 9 would be generated, then it is multiplied by 4 ( 9 * 4 = 36). Next the Address present in locations 36 to 39 would be picked and control would be passed to this address, Which is address of actual ROM-BIOS Service Routine. But here we do a little trick, We change this address at 36 to 39 locations. Now OUR function will be called which changes case of alphabets.

The Second TSR


Let us try to write a TSR to permanently keep the Caps Lock on. For this we have to catch Interrupt Number 9 and then call our routine to keep Caps on.

/* TSR2.c */ #include dos.h void interrupt our(); void interrupt (*prev)(); char far *kb = (char far *)0x417; main( ) { prev = getvect(9); /* To get address of Interrupt number 9 */ setvect(9,our); /* To set address of our function */ keep(0,500); /* Make program resident in Memory */ } void interrupt our() { (*prev)(); *kb = *kb | 64; }

How it Works ?
The Status of Caps Lock is stored in the sixth bit of byte at 0x417. Interrupt Number 9 is generated once when we hit a key and once when we release it. Now when any key is released, Our routine makes Caps Lock on.

7 Ins

2 Ctrl

1 Left Shift

0 Right Shift

Caps Num Scroll Alt Lock Lock

#include dos.h void interrupt our(); void interrupt (*prev)(); char far *kb = (char far *)0x417; main( ) { prev = getvect(9); /* To get address of Interrupt number 9 */ setvect(9,our); /* To set address of our function */ keep(0,500); /* Make program resident in Memory */ } void interrupt our() { (*prev)();
(*prev)();

/* TSR2 .c */ The

Third TSR

Let us try to write a Time Bound TSR which Changes Color of Screen after a Fixed Time as given below: #include "dos.h /* TIMEB.C */ void interrupt our(); void interrupt (*prev)(); char far *scr = (char far *)0xB8000000L; int ticks; unsigned char color; main() { prev = getvect(8); setvect(8,our); keep(0,500); }

void interrupt our() /* TIMEB.C Contd. */ { int i; ticks++; if(ticks == 182) { for(i=1;i<=3999;i++) *(scr+i) = color; color++; ticks=0; } (*prev)(); }

#include "dos.h /* RAIN.C */ #include "stdlib.h" void interrupt our(); void interrupt (*prev)(); char far *scr = (char far *)0xB8000000L; int ticks; main() { prev = getvect(8); setvect(8,our); keep(0,1000); }

void interrupt our() { int i,col=1,row; char far *v,ch; while(1) /* Infinite loop */ { row = 1; ch = *(scr+row * 160 +col*2); for(;row<=24;row++) { v = scr + row*160+col*2; *(v-160)=' '; *v=ch; delay(100); } col++; } (*prev)(); }

Das könnte Ihnen auch gefallen