You are on page 1of 11

Experiment No.

1
Aim- To understand the concept of macros.
Theory-: A macro is a fragment of code which has been given a name. Whenever the name is used, it is replaced by the contents of the macro. There are two kinds of macros. They differ mostly in what they look like when they are used. Object-like macros resemble data objects when used, function-like macros resemble function calls. You may define any valid identifier as a macro, even if it is a C keyword. The preprocessor does not know anything about keywords. This can be useful if you wish to hide a keyword such as const from an older compiler that does not understand it. However, the preprocessor operator defined can never be defined as a macro Q1>Write a program to implement macro to add two numbers. Write a program #include<stdio.h> #define macro1(a,b) (a+b) #include<conio.h> Void main() { Int n1,n2; clrscr(); printf(enter two numbers); scanf(%d%d,&n1,&n2); printf(Sum of two numbers %d,macro1(n1,n2)); getch(); }

Experiment No.2
Aim-To check the validity of string for a given grammar
Theory-: In formal language theory, a context-free grammar (CFG) is a formal grammar in which every production rule is of the form Vw where V is a single nonterminal symbol, nonterminals (w can be empty). and w is a string of terminals and/or

The languages generated by context-free grammars are known as the context-free languages. A formal grammar is considered "context free" when its production rules can be applied regardless of the context of a nonterminal. Context-free grammars are important in linguistics for describing the structure of sentences and words in natural language, and in computer science for describing the structure of programming languages and other formal languages. In linguistics, some authors use the term phrase structure grammar to refer to context-free grammars, whereby phrase structure grammars are distinct from dependency grammars. In computer science, a popular notation for context-free grammars is BackusNaur Form, or BNF.

A context-free grammar G is defined by the 4-tuple:[3] where 1. is a finite set; each element is called a non-terminal character or a variable. Each variable represents a different type of phrase or clause in the sentence. Variables are also sometimes called syntactic categories. Each variable defines a sublanguage of the language defined by . 2. is a finite set of terminals, disjoint from , which make up the actual content of the sentence. The set of terminals is the alphabet of the language defined by the grammar . 3. is a finite relation from to , where the asterisk represents the Kleene star operation. The members of are called the (rewrite) rules or productions of the grammar. (also commonly symbolized by a ) 4. is the start variable (or start symbol), used to represent the whole sentence (or program). It must be an element of .

Q2>Write a program to check whether the entered string is accepted or not for a given grammar. Strings acceptable by grammar are of form: ab*c(a+b) #include<stdio.h> #include<conio.h> #include<string.h> #include<stdlib.h> char a[100]; int n, i; void main() { clrscr(); printf(\nenter string); scanf(%s,&a); n=strlen(a); if(a[0]==a && (a[n-1]==a || a[n-1]==b) && a[n-2]==c) { for(i=1; i<n-2; i++) { if(a[i]!=b) { printf(\n string is not accepted); getch(); exit(0); } } printf(\n string is accepted); } else printf(\n string is not accepted); getch(); } Output:

Experiment No.3
Aim-To understand the implementation of far, near, huge pointer
Theory-: Four registers are used to refer to four segments on the 16-bit x86 segmented memory architecture. DS (data segment), CS (code segment), SS (stack segment), and ES (extra segment). A logical address on this platform is written segment:offset, in hexadecimal. In real mode, in order to calculate the physical address of a byte of memory, one left-shifts the contents of the appropriate register 4 bits, and then adds the offset. For example the logical address 7522:F139 yields the 20-bit physical address:
75220 + F139 = 84359

Note that this process leads to aliasing of memory, such that any given physical address may have multiple logical representations. This makes comparison of pointers difficult. Actually there are 3 types of pointers: 1) near 2) far 3) huge Near Pointers: These pointers are 2 bytes long always i.e. any pointer that you declared normally they are 2 bytes long. This permits only 2^16 values which implies that you can access only the code which is in your data segment (since 1 segment is 64KB = 2^16). Any attempt to access data will result in segment protection error from the OS.

Far and Huge Pointers: We take both these two since they are common and differ only when some arithmetic is performed using these pointers.

Far and Huge pointers are both 4 bytes long. This means they can handle about 2^32= 2GB of memory locations. Now since we dont have that much amount of memory when we are real mode (when computer starts up) we are safe since only 1MB is available (only 20 address lines available instead of 32 present address lines). We can access the whole of memory when we are in protected mode. This mode gives the whole 2GB memory as a flat i.e. no segments are available when in protected mode. But since we are in real mode our max

memory limit is 1MB and our memory is fragmented into different segments of 64Kb each in size. To access any memory location in the 1MB limit we first find the starting address of the segment then to this address (base address of segment) an offset of 16 bits is added. The way these addresses are added is a bit tricky. First the segments address is left shifted by 4 bits (padded with 4 zeros in the end) and then the offset is added to it. Now this is a full 20 bit address and processor use this address to locate a memory location in the 1MB limit. The shifting need not be done by us since processor does that for us we just need to supply the segments base address and an offset within that segment.

All segments starts at some multiple of 16 also called a paragraph boundary. This is because for each 16 bit segment address it is first left shifted by 4 bits (2^4=16 bytes). One more thing where these pointers differ from each other is in the way they are stored. Far pointers are not normalized while huge pointers are normalized. Normalization means that they have most part of the address in the segment. This is not a problem if you will perform logical / arithmetic operations on such pointers but if you do then using huge will benefit you since the results will be same as expected while with far pointers you may get strange results. Huge pointer arithmetic is therefore more complex which requires macros and thus slows down the performance. Declaring far /huge pointers The Turbo C provides us the keywords far and huge to declare such pointers. Just add these keywords and declare these pointers like you normally do.

Example int far *ptr; (this ptr is a far pointer to an int) int huge *ptr(this ptr is huge pointer to an int) Why do we need these pointers while writing ISRs? Well as I said before with normal / near pointer declaration we cant access the code outside of our data segment. Since ISR s require us to go beyond our data segment we must use these pointers. Only far can be used huge may not be required since theres not much in the data segment which we need to access. Using huge pointers we can access more than 1MBs of data in the data segment while 1MB when we use far.

Q3>Write a program to study the implementation of far, huge, near pointers.


Far pointer

Void main() { char far *a = 0x00000120; char far *b = 0x00100020; char far *c = 0x00120000; if(a==b) printf(\n hello); if(a==c) printf(\n hi) if(b==c) printf(\n hello hi); if(a>b && a>c && b>c) printf(bye); } Near pointer Void main() { char near *a = 0x00000120; char near *b = 0x00100020; char near *c = 0x00120000; if(a==b) printf(\n hello); if(a==c) printf(\n hi) if(b==c) printf(\n hello hi); if(a>b && a>c && b>c) printf(bye); }

Huge pointer Void main() { char huge *a = 0x00000120; char huge *b = 0x00100020; char huge *c = 0x00120000; if(a==b) printf(\n hello); if(a==c) printf(\n hi) if(b==c) printf(\n hello hi); if(a>b && a>c && b>c) printf(bye); }

Experiment No.4
Aim- To understand concept of absolute loader
Theory-: Definition of Loader: Loader is utility program which takes object code as input prepares it for execution and loads the executable code into the memory. Thus loader is actually responsible for initiating the execution process. Absolute Loader: Absolute loader is a kind of loader in which relocated object files are created, loader accepts these files and places them at specified locations in the memory. This type of loader is called absolute because no relocation information is needed; rather it is obtained from the programmer or assembler. The starting address of every module is known to the programmer, this corresponding starting address is stored in the object file, then task of loader becomes very simple and that is to simply place the executable form of the machine instructions at the locations mentioned in the object file. In this scheme, the programmer or assembler should have knowledge of memory management. The resolution of external references or linking of different subroutines are the issues which need to be handled by the programmer. The programmer should take care of two things: first thing is : specification of starting address of each module to be used. If some modification is done in some module then the length of that module may vary. This causes a change in the starting address of immediate next . modules, its then the programmer's duty to make necessary changes in the starting addresses of respective modules. Second thing is ,while branching from one segment to another the absolute starting address of respective module is to be known by the programmer so that such address can be specified at respective JMP instruction.

The absolute loader is simple to implement in this schemel) Allocation is done by either programmer or assembler 2) Linking is done by the programmer or assembler 3) Resolution is done by assembler 4) Simply loading is done by the loader

Q4>Write a c program to implement absolute loader. #include<stdio.h> #include<ctype.h> #include<conio.h> #include <stdlib.h> void main() { char *record,*obj,*names; int addr,len,i; FILE *fp; clrscr(); fp=fopen("IN.TXT","r"); fscanf(fp,"%s%s%d%d",record,name,&addr,&len); if(strcmp(record;"H")==0) { printf("program name=%s",name); printf("starting address=%d",addr); } while(!feof(fp)) { fsane(fp,"%s",record); if(strcmp(record,"E")==0) { fscan(fp,%x",&addr); printf("address of the first statment %d",addr); break; } else if(strcmp(record,"T")==0) {

fscanf(fp,"%x5x%s",&addr,&len,obj); for(i=0;i<len;i+=2) { printf("%x\t",addr++); printf("%c%c",obj[i],obj[i+1]); } } } fclose(fp); getch(); }

Experiment No.5
Aim-Conversion of a physical address into segment and offset
Theory-: There are often many different Segment:Offset pairs which can be used to address the same location in your computer's memory. This scheme is a relative way of viewing computer memory as opposed to a Linear or Absolute addressing scheme. When an Absolute addressing scheme is used, each memory location has its own unique designation; which is a much easier way for people to view things. So, why did anyone ever create this awkward "Segment:Offset scheme" for dealing with computer memory? As an answer, here's a brief lesson on the 8086 CPU with an historical slant:

Segment:Offset addressing was introduced at a time when the largest register in a CPU was only 16-bits long which meant it could address only 65,536 bytes (64 KiB[1]) of memory, directly. But everyone was hungry for a way to run much larger programs! Rather than create a CPU with larger register sizes (as some CPU manufacturers had done), the designers at Intel decided to keep the 16-bit registers for their new 8086 CPU and added a different way to access more memory: They expanded the instruction set, so programs could tell the CPU to group two 16-bit registers together whenever they needed to refer to an Absolute memory location beyond 64 KiB.

Q5>Write a program in c to print Segment:Offset value. #include<dos.h> main() { Char far * ptr; Unsigned seg,off; Ptr=MK_FP(0xb000,0); Seg=FP_SEG(ptr); Off=FP_OFF(ptr); Printf(\n far ptr %Fp,ptr); Printf(\n segment %x,offset %x,seg,off); }

You might also like