Assembly LanguageSolutionThere are many highlevel structured

Assembly Language

Solution

There are many high-level, structured languages for programming today\'s PCs. Two popular examples are C++ and Pascal. However, assembly language still has its place in today\'s programming world. Since it mimics the operations of the CPU at the machine level, assembly language lets you get right to the \"heart\" of your PC. In fact, there are some tasks that you can do only by using assembly language. While it\'s true that the Pascal language is capable enough to handle interrupts, it can\'t be used to pass keyboard input to DOS, for example. Since Pascal has no native way to do this, you must still insert an assembler module routine to perform the function. Likewise, you can\'t easily remove a high-level resident program from memory. Once again, you have to write the routine in assembly language to do this. For many applications, programming code must still be as compact as possible. For example, in programming resident programs, each kilobyte of RAM below the 640K boundary is vital. Programs written in high-level languages usually require a runtime library which may add several additional kilobytes to the size. Assembly language programs don\'t need these bulky library routines. However, the most important advantage of assembly language is speed. Although high-level languages can be optimized for speed of execution, even the best optimization cannot replace the experience of a programmer. Here\'s a simple example. Let\'s say that you want to initialize two variables in Pascal to a zero value. The compiler will generate the following assembly code: xor ax,ax mov var1,ax xor ax,ax mov var2,ax Here, the Pascal compiler optimized the execution speed by using the XOR instruction to zero the ax register (the fastest way to do this) and storing this value as var1. However, due to compiler\'s limitations, the AX register was again zeroed before the second assignment although this was redundant. For truly time-critical tasks such as sprite movement and high-speed graphics, the only choice may be to use assembly language. There are two basic ways to do this: 1. Use an internal assembler such as the one built into Borland Pascal and its asm directive. 2. Use a stand-alone assembler such as Turbo Assembler or Microsoft Assembler. Each way has its own advantages and disadvantages but using the stand-alone assembler is usually the better choice.

The stand-alone assembler is designed from the ground up for writing full assembly language programs - not as an add-on to a high-level language. A stand-alone assembler has a complete programming environment with many convenient features. For example, it has directives such as \"db 20 dup\" that makes programming easier. Only a limited number of directives are available from built-in assemblers. Standalone assemblers also offer the advantage of macros which speed up assembly language programming tasks. We\'ve chosen to use a stand-alone assembler in this book wherever possible. Of course there are exceptions such as if the assembly language routine module has to access a procedure\'s local variables as in Borland\'s GetSprite and PutSprite procedures. Multiplication And Division In Assembly Language Today\'s 486 DX4es and Pentiums are fast. These speed demons can perform a multiplication operation in only six clock cycles. This is a far cry from the 100+ cycles that were required using the ancient 8086 processors or about 20 cycles using yesterday\'s 286es. However, if you really want to impress people with fast multiplication, you can use the shift instructions. The number of bits by which you\'re shifting corresponds to the exponent of the multiplicand to base 2; to multiply by 16, you would shift 4 bits since 16 equals 2 to the 4th power. The fastest method of multiplying the AX register by 8 is the instruction SHL AX,3 which shifts each bit to a position eight times higher in value. Conversely, you can perform division by shifting the contents to the right. For example, SHR AX,3 divides the contents of the AX register by 8. In the early days of computing, numerical analysts suggested other ways to speed up computations. One common technique was to use factoring. For example, multiplication by 320 can be factored like this: 1. Multiplication of the value by 256 (shift by 8 bits) 2. Multiplication of a copy of the value by 64 (shift by 6 bits) 3. Addition of the two results from above Mathematicians call this factoring according to the distributive law. Fixed Point Arithmetic The preceding examples assume that the values you\'re working with are integers. But for many applications, it\'s not always appropriate or possible to use integers. In programming graphics, for example, to draw a line on the screen you need to know the slope of the line. Practically speaking, the slope is seldom an integral number. Normally, in such cases, you would use real (Pascal) or float (C) values which are based on floating point representation. Floating point numbers allow a variable number of decimal places. The decimal point can be placed almost anywhere - which gives rise to the term floating point.

Compared to integers, arithmetic using floating point numbers is very slow. Some PCs have math coprocessors that can perform arithmetic directly. However, if the PC doesn\'t have a coprocessor then the floating point computations must be performed by software. This accounts for the higher computing times for floating point arithmetic. Working with floating point number in assembly language isn\'t very easy. So you can use a high-level language for floating point operations or you can write your own routines. Using high-level language operations is not always easy in Pascal, for example, because the four basic arithmetic operations are not declared as Public. Since both of these alternatives options require a considerable amount of effort, let\'s look at another alternative. Many application require only a limited amount of computational precision. In other words, they may not really need eleven significant decimal places. For applications where the values have a narrow range, you may be able to use fixed point numbers. Fixed point numbers consist of two parts: 1. One part specifies the integer portion of the number 2. The other part specifies the decimal (fraction) part of the number When using fixed point number, you must first set (or fix) the number of decimal places. Let\'s see how a fixed point number can change by varying the number of decimal places. The fixed portion and decimal portion of 17 and 1 respectively. By changing the number of decimal places, the value of the fixed point number is changed: Number of decimal places 1 2 3 4 Value of fixed point number 17.1 17.01 17.001 17.0001 So it\'s important that there be a clear understanding of how many fixed places the fractional portion will represent. Now for a quick look at how the mathematical signs are used for fixed point numbers. In fixed point notation, the value -100.3 can be divided into two parts: -100 and -3 (using one decimal place). Adding these two together yields the actual rational number. In this example, adding -100 and -0.3, produces a result of -100.3, which achieves our objective. The most important advantage of working with these numbers is obvious: They consist of two simple integer numbers which are paired in a very simple way. During addition, any overflow of the fractional portion is added to the integer portion. Using this scheme, even a lowly powered 8086 processor can work efficiently and quickly without a coprocessor. Realizing that the CPU is not set up to handle fixed point operations automatically, we\'ll have to program a way to perform the arithmetic operations. We\'ll see one way to do this in the next section. The method is so flexible that you can even perform more complicated operations, such as root determination by approximation, where you\'ll really notice the speed advantage of fixed point arithmetic.

The four fundamental arithmetic operations Because they\'re so close to integer numbers, developing basic arithmetic operations for the fixed point numbers is no big deal. The math instructions are already built into the processor so the remaining consideration is deciding how to work with the paired numbers. The program in this chapter shows one way of packaging a math library for fixed point numbers. This program implements the four basic arithmetic operations in Pascal. By rewriting the routines in assembly language, you can make the routines fly even faster, but the Pascal example here demonstrates the method. Addition The easiest operation is addition. To add two fixed point numbers, you simply add the integer portions and the fractional portions separately. Here\'s the only complicating factor. If the two fractional portions produce a value greater than one, then you have to handle the \"overflow\". For example, an overflow occurs for fixed point numbers with two decimal places when the two fractional values sum to a value of 100 or higher. In this case, the overflow is handled by adding one to the integer portion and subtracting 100 from the fractional portion. The reverse is true with negative numbers. In this case, you subtract one from the integer portion and add 100 to the fractional portion. Subtraction Subtraction is similar to addition, except the two separate portions are subtracted from one another. Overflow is handled in the same way. Multiplication A more elaborate method is used for multiplication. First, each factor is converted to a whole number. Next the two factors are multiplied. Then the product is reconverted back to a fixed point value. During the reconversion, the product is adjusted by dividing by the number of decimals since the factors were increased when they were first converted into whole numbers. Division Division is performed by a method that parallels multiplication. As in multiplication, you convert the fixed point dividend and the divisor into whole numbers, thereby temporarily eliminating the decimals. Again after the division, the quotient is adjusted by dividing the number of decimals. The program BASARITH.PAS, listed below, illustrates this technique: Type Fixed=Record {structure of a fixed point number} BeforeDec, AfterDec:Integer End; Var Var1, {sample variables} Var2:Fixed;

Const AfterDec_Max=100; {2 places after decimal point} AfterDec_Places=2; Function Strg(FNumber:Fixed):String; {converts a fixed point number to a string} Var AfterDec_Str, {string for forming the fractional part} BeforeDec_Str:String; {string for forming the integral part} i:Word; Begin If FNumber.AfterDec < 0 Then {output fractional part without sign} FNumber.AfterDec:=-FNumber.AfterDec; Str(FNumber.AfterDec:AfterDec_Places,AfterDec_Str); {generate decimal string} For i:=0 to AfterDec_Places do {and replace spaces with 0s} If AfterDec_Str[i] = \' \' Then AfterDec_Str[i]:=\'0\'; Str(FNumber.BeforeDec,BeforeDec_Str); {generate integral string} Strg:=BeforeDec_Str+\',\'+AfterDec_Str; {combine strings} End; Procedure Convert(RNumber:Real;Var FNumber:Fixed); {converts Real RNumber to fixed point number FNumber} Begin FNumber.BeforeDec:=Trunc(RNumber); {define integral part} FNumber.AfterDec:=Trunc(Round(Frac(RNumber)*AfterDec_Max)); {define fractional part and store as whole number} End; Procedure Adjust(Var FNumber:Fixed); {puts passed fixed point number back in legal format} Begin If FNumber.AfterDec > AfterDec_Max Then Begin Dec(FNumber.AfterDec,AfterDec_Max); {if fractional part overflows to positive} Inc(FNumber.BeforeDec); {reset and decrement integral part} End; If FNumber.AfterDec < -AfterDec_Max Then Begin Inc(FNumber.AfterDec,AfterDec_Max); {if fractional part overflows to positive} Dec(FNumber.BeforeDec); {reset and increment integral part} End; End; Procedure Add(Var Sum:Fixed;FNumber1,FNumber2:Fixed); {Adds FNumber1 and FNumber2 and places result in sum} Var Result:Fixed; Begin Result.AfterDec:=FNumber1.AfterDec+FNumber2.AfterDec; {add fractional part} Result.BeforeDec:=FNumber1.BeforeDec+FNumber2.BeforeDec; {add integral part} Adjust(Result); {Put result back in correct format} Sum:=Result; End; Procedure Sub(Var Difference:Fixed;FNumber1,FNumber2:Fixed); {Subtracts FNumber1 from FNumber2 and places result in difference} Var Result:Fixed; Begin Result.AfterDec:=FNumber1.AfterDec-FNumber2.AfterDec; {subtract fractional part} Result.BeforeDec:=FNumber1.BeforeDec-FNumber2.BeforeDec; {subtract integral part} Adjust(Result);

{put result back in correct format} Difference:=Result; End; Procedure Mul(Var Product:Fixed;FNumber1,FNumber2:Fixed); {multiplies FNumber1 and FNumber2 and places result in product} Var Result:LongInt; Begin Result:=Var1.BeforeDec*AfterDec_Max + Var1.AfterDec; {form first factor} Result:=Result * (Var2.BeforeDec*AfterDec_Max + Var2.AfterDec); {form second factor} Result:=Result div AfterDec_Max; Product.BeforeDec:=Result div AfterDec_Max; {extract integral and fractional parts} Product.AfterDec:=Result mod AfterDec_Max; End; Procedure Divi(Var Quotient:Fixed;FNumber1,FNumber2:Fixed); {divides FNumber1 by FNumber2 and places result in quotient} Var Result:LongInt; {intermediate result} Begin Result:=FNumber1.BeforeDec*AfterDec_Max + FNumber1.AfterDec; {form counter} Result:=Result * AfterDec_Max div (FNumber2.BeforeDec*AfterDec_Max+FNumber2.AfterDec); {divide by denominator, provide more places beforehand} Quotient.BeforeDec:=Result div AfterDec_Max; {extract integral and fractional parts} Quotient.AfterDec:=Result mod AfterDec_Max; End; Begin WriteLn; Convert(-10.2,Var1); {load two demo numbers} Convert(25.3,Var2); {some calculations for demonstration purposes:} Write(Strg(Var1),\'*\',Strg(Var2),\'= \'); Mul(Var1,Var1,Var2); WriteLn(Strg(Var1)); Write(Strg(Var1),\'-\',Strg(Var2),\'= \'); Sub(Var1,Var1,Var2); WriteLn(Strg(Var1)); Write(Strg(Var1),\'/\',Strg(Var2),\'= \'); Divi(Var1,Var1,Var2); WriteLn(Strg(Var1)); Write(Strg(Var1),\'+\',Strg(Var2),\'= \'); Add(Var1,Var1,Var2); WriteLn(Strg(Var1)); End. Addition, subtraction, multiplication and division are implemented in the procedures Add, Sub, Mul and Divi respectively. The main program tests each of the operations. Procedure Adjust makes the decimal adjustments after addition and subtraction. Procedure Convert converts a floating point number to a fixed point number and Strg generates a string out of this fixed point number so it can be displayed on the screen.

Assembly LanguageSolutionThere are many high-level, structured languages for programming today\'s PCs. Two popular examples are C++ and Pascal. However, assembl
Assembly LanguageSolutionThere are many high-level, structured languages for programming today\'s PCs. Two popular examples are C++ and Pascal. However, assembl
Assembly LanguageSolutionThere are many high-level, structured languages for programming today\'s PCs. Two popular examples are C++ and Pascal. However, assembl

Get Help Now

Submit a Take Down Notice

Tutor
Tutor: Dr Jack
Most rated tutor on our site