4.5 Sample Program
The following sample program demonstrates BCD I/O. The following program provides two procedures, BCDin and BCDout. These two procedures read an 18-digit BCD value from the user (with possible leading minus sign) and write a BCD value to the standard output device.
program bcdIO; #include( "stdlib.hhf" ) // The following is equivalent to TBYTE except it // lets us easily gain access to the individual // components of a BCD value. type bcd:record LO8: dword; MID8: dword; HO2: byte; Sign: byte; endrecord; // BCDin- // // This function reads a BCD value from the standard input // device. The number can be up to 18 decimal digits long // and may contain a leading minus sign. // // This procedure stores the BCD value in the variable passed // by reference as a parameter to this routine. procedure BCDin( var input:tbyte ); @nodisplay; var bcdVal: bcd; delimiters: cset; begin BCDin; push( eax ); push( ebx ); // Get a copy of the input delimiter characters and // make sure that #0 is a member of this set. conv.getDelimiters( delimiters ); cs.unionChar( #0, delimiters ); // Skip over any leading delimiter characters in the text: while( stdin.peekc() in delimiters ) do // If we're at the end of an input line, read a new // line of text from the user, otherwise remove the // delimiter character from the input stream. if( stdin.peekc() = #0 ) then stdin.readLn(); // Get a new line of input text. else stdin.getc(); // Remove the delimeter. endif; endwhile; // Initialize our input accumulator to zero: xor( eax, eax ); mov( eax, bcdVal.LO8 ); mov( eax, bcdVal.MID8 ); mov( al, bcdVal.HO2 ); mov( al, bcdVal.Sign ); // If the first character is a minus sign, then eat it and // set the sign bit to one. if( stdin.peekc() = '-' ) then stdin.getc(); // Eat the sign character. mov( $80, bcdVal.Sign ); // Make this number negative. endif; // We must have at least one decimal digit in this number: if( stdin.peekc() not in '0'..'9' ) then raise( ex.ConversionError ); endif; // Okay, read in up to 18 decimal digits: while( stdin.peekc() in '0'..'9' ) do stdin.getc(); // Read this decimal digit. shl( 4, al ); // Move digit to H.O. bits of AL mov( 4, ebx ); repeat // Cheesy way to SHL bcdVal by four bits and // merge in the new character. shl( 1, al ); rcl( 1, bcdVal.LO8 ); rcl( 1, bcdVal.MID8 ); rcl( 1, bcdVal.HO2 ); // If the user has entered more than 18 // decimal digits, the carry will be set // after the RCL above. Test that here. if( @c ) then raise( ex.ValueOutOfRange ); endif; dec( ebx ); until( @z ); endwhile; // Be sure that the number ends with a proper delimiter: if( stdin.peekc() not in delimiters ) then raise( ex.ConversionError ); endif; // Okay, store the ten-byte input result into // the location specified by the parameter. mov( input, ebx ); mov( bcdVal.LO8, eax ); mov( eax, [ebx] ); mov( bcdVal.MID8, eax ); mov( eax, [ebx+4] ); mov( (type word bcdVal.HO2), ax ); // Grabs "Sign" too. mov( ax, [ebx+8] ); pop( ebx ); pop( eax ); end BCDin; // BCDout- // // The converse of the above. Prints the string representation // of the packed BCD value to the standard output device. procedure BCDout( output:tbyte ); @nodisplay; var q:qword; begin BCDout; // This code cheats *big time*. // It converts the BCD value to a 64-bit integer // and then calls the stdout.puti64 routine to // actually print the number. In theory, this is // a whole lot slower than converting the BCD value // to ASCII and printing the ASCII chars, however, // I/O is so much slower than the conversion that // no one will notice the extra time. fbld( output ); fistp( q ); stdout.puti64( q ); end BCDout; static tb1: tbyte; tb2: tbyte; tbRslt: tbyte; begin bcdIO; stdout.put( "Enter a BCD value: " ); BCDin( tb1 ); stdout.put( "Enter a second BCD value: " ); BCDin( tb2 ); fbld( tb1 ); fbld( tb2 ); fadd(); fbstp( tbRslt ); stdout.put( "The sum of " ); BCDout( tb1 ); stdout.put( " + " ); BCDout( tb2 ); stdout.put( " is " ); BCDout( tbRslt ); stdout.newln(); end bcdIO; Program 4.8 BCD I/O Sample Program4.6 Putting It All Together
Extended precision arithmetic is one of those activities where assembly language truly shines. It's much easier to perform extended precision arithmetic in assembly language than in most high level languages; it's far more efficient to do it in assembly language, as well. Extended precision arithmetic was, perhaps, the most important subject that this chapter teaches.
Although extended precision arithmetic and logical calculations are important, what good are extended precision calculations if you can't get the extend precision values in and out of the machine? Therefore, this chapter devotes a fair amount of space to describing how to write your own extended precision I/O routines. Between the calculations and the I/O this chapter describes, you're set to perform those really hairy calculations you've always dreamed of!
Although decimal arithmetic is nowhere near as prominent as it once was, the need for decimal arithmetic does arise on occasion. Therefore, this chapter spends some time discussing BCD arithmetic on the 80x86.
|