Computer Engineering II
  Machine Problem 2

Schedule    Lab schedule
Homework    NASM docs
Machine Problems    Resources
Final Project    Photos
Gradebook    Feedback
Syllabus    Archives
Lectures    Download NASM
Home    Restricted access

Machine Problem 2: Financial Calculations

Assigned Thursday, 9/13/2001
Due Date Wednesday, 10/3/2001
Purpose Handle complex user input. Perform calculations on user data structures. Recursion.
Points 75
Updates:
9/21: Due date changed to 10/3
9/20: FDPow even/odd b corrected to n
9/18: FDPow description

Introduction

After reeling from several thousand dollars of debt to the Illini Union Bank, you get a strange feeling that something was not right. The Illini Union Bank was not paying interest on its accounts!

It's time to fix this major oversight. The IUB is still expecting the unwitting engineering students to work out the minor flaws in its original roll-out, such as the inability to collect from students who have taken out several $4096 withdrawals. Of course, we don't want to fix that bug.

Problem Description

Now no longer working with the front end to the bank ledger, we will instead work with back-end functionality. The back-end interface takes individual commands that work on fixed-point decimal values. The operations it supports are as follows:

  • Compound Interest Calculation

    Your money in a bank account that earns interest is often compounded monthly on a yearly percentage. The formula that governs this is:

    A = P (1+(i/m))n m,

    where P is the principal, i is the interest per cycle, and m is number of periods in a cycle. For our MP's purposes we don't want to implement division; we will pre-divide out i/m, and pre-multiply the exponent n m. Thus our simplified equation is:

    A = P (1+i)n,

    where i is now the interest per period, and n is the number of periods.

  • Currency Conversion

    The Illini Union Bank is looking to set up branches in several countries. At each of these branches they will allow you to withdraw your money in the local currency. In order to know how much to give you, their backend must support currency conversions.

    To convert between currencies, the IUB is converting through dollars. The formula to convert between arbitrary currencies is as follows:

    A = P (from X to dollars) (from dollars to Y)

    These conversion rates are stored in tables, allowing for easy lookup.

Fixed Point Decimal Numbers

Since we want to read, work with, and store numbers that look like 102.34, we need to define a representation and to write functions to perform useful math on them. First we'll address the representation we will be using, and then we'll address what functions we need to write and how to write them.

Representation

One way to store these numbers is by storing a larger number that is 100 times larger than the stored number. For example to store our number 102.34, we'd store the binary for 10234, and divide off the 34 to display it. As it turns out, doing it this way would make it hard to use ascbin, binasc, and other assorted functions since we would need to store more than 16 bits to keep the same range our normal 16 bit numbers have.

To address these size issues, we will instead use a object which is 32 bits large. In one half we will store the integer component. In the other half, we will store the decimal component multiplied by 100, with a maximum value of 99. The number 102.34 would be stored with the two word-sized numbers 102 and 34. The primary advantage to doing it this way is that our normal tools will work, ascbin and binasc in particular.

Functions

For the financial calculations in this MP we need several basic utility functions. These include the following:

FDRead

To do any work with a FDNumber, we must first read it in. While ascbin can turn a string into a number, this only works for integers. FDRead must handle numbers that look like 102, 102.3, and 102.34. To do this, you will probably wish to call ascbin on the components and then store them into the appropriate halves of the FDNumber.

FDRead must report success and error cases like ascbin does. Unlike ascbin the output is in the FDNumber pointed to by di, and ax should remain unchanged.

Special cases to be aware of:

  • 102 with no decimal. Don't forget to zero the decimal portion.
  • 102. with a trailing decimal point cannot be handled easily due to the way ascbin functions.

    If you choose to handle this, expect to do something unusual. If you do not choose to handle these, expect debugging such numbers to not work as you may expect.

  • 102.3 with a single digit after the decimal point.

    When a single digit occurs after the decimal point, it needs to be multiplied by 10, as otherwise the 3 will be 3/100, not 3/10. One way to handle this is by tracking the change bx across calls to ascbin and acting accordingly.

  • 102.345 a decimal portion too large; these numbers are illegal in our limited framework.
  • 102.003 a decimal portion too long; these cannot easily be detected, except perhaps by similar means to the 102.3 case. Just know that these numbers are not handled correctly, and you don't have to.

FDWrite

After reading in a number, it's good to be able to display the number to a curious user. Just as FDRead can be implemented with two calls to ascbin with additional logic, FDWrite can be implemented as two calls to binasc also with additional logic.

FDWrite must match binasc in terms of outputs, with bx pointing to the first non-blank character in the 10-byte buffer, and cl holding the number of non-blank characters in the generated string.

The only special case to FDWrite is for numbers like 102.03. Since binasc will generate the string "     3$", you must manually add the prepended zero. You may find it useful to use an additional buffer to write parts of the numbers and copy them into the final buffer pointed to by bx.

FDAdd

Reading and writing FDNumbers is nice, but now let's do some math. Adding two numbers is very straightforward, and is good practice for the more complicated operations we will be doing next.

If you consider the two FDNumbers A and B, with corresponding pieces Ah, Al, Bh, and Bl such that A=Ah+Al/100. Their sum S is calculated as follows:

Sh = Ah+Bh
Sl = Al+Bl

Except one thing: Al+Bl may exceed 99, but Sl is not allowed to. In such cases, for every 100 in Sl, subtract it out and add 1 to Sh. You may find it more intuitively obvious to do this with div or you may not.

Note that FDAdd reads from the FDNumbers at offsets si and di and writes to the FDNumber at offset di, much like an add [di], [si] might function if it were possible.

FDMul

Multiplication is an extension of adding. Consider again two such numbers A and B, separated as above. Their product P is calculated much like you would calculated the product (x+y)(x-y)

Ph = Ah*Bh + Ah*Bl/100 + Al*Bh/100
Pl = Ah*Bl%100 + Al*Bh%100 + Al*Bl/100,

where x % 100 is the remainder when x is divided by 100, or its modulus.

Note that Al*Bl % 100 may be greater than or equal to 50. In such cases Pl should be incremented, and such changes should be propagated through to Ph as necessary.

The additions in the formula for P may be done by calling FDAdd or you may find it easier to implement them manually.

FDPow

FDPow is our exponentiation operator. In an algorithms class you may have become acquainted with the recursive algorithm we will be using. Don't worry if you haven't, as we describe it fully here.

Recursive algorithms work by defining a base case and an iterative step. The Fibonacci function is the classic example: the base cases are F(0)=0 and F(1)=1. For all other F(n), F(n) = F(n-1) + F(n-2). Thus when finding F(12), it recursively can ask for F(10) and F(11). Once F(10) and F(11) return their answers, the body for the Fibonacci function can add them together and return the final answer for F(12).

Applying this back to the problem at hand: we have a number b which we wish to raise to the nth power. The base case is b raised to the first power, or merely b itself. Thus when FDPow is called with exponent 1, it can return the base as the answer without further calculations. The recursive step is one of two things, depending on whether the exponent is an even or odd number:

bn = b * bn/2 * bn/2,

for all odd n, and truncated values n/2, or

bn = bn/2 * bn/2,

for all even n.

Once these cases are all handled, i.e. n=1, and all even or odd numbers above 1, the nature of recursion (or the Recursion Fairy, according to Professor Jeff Erickson, who should know) will give you your final answer almost magically. The only trick is the actual implementation of recursion in assembly. Where high level languages will automagically keep your inputs and outputs separate, assembly style procedures make use of single pieces of hardware (registers) for both input and outputs.

UPDATE (9/18) The library code now handles an additional base case n=0. You are encouraged, but not required, to handle it as well. The proper return value is 1.00.

FDPow takes two addresses as input. Like the previous functions they are si and di. Unlike the previous functions, we neither store back to the number at offset di, nor is the operation commutative: an is not na. The convention we have instead chosen is to calculate [di][si], storing the result in dx:ax. Store the integer portion in dx and the hundredths in ax.

Note: thinking recursively takes practice. The same code must function correctly whether it is called with 1, 4, or n as the exponent. And more importantly, since it calls itself, anything it writes to will be similarly written to by its recursive call. In general you can work around this by pushing values to the stack and restoring them from the stack as the recursive procedure exits. Don't give up!

Input Handling

Now that we've got a set representation and several utility functions for our fixed-point decimal numbers, it's time to write the rest of the program that makes it possible to use them. To perform a compound interest calculation or currency conversion, the user will type something like: i 100 0.03 12, which represents a starting value of $100, at a 3% interest rate, compounded 12 times. How do we read this command in and then process it?

There are four parts to this answer: DoCommand, ReadLine, GetLetter, and GetNumber. Additionally, once a command line is understood, either CalculateInterest or ConvertCurrency is called to handle the computation.

DoCommand

DoCommand is the heart of the operation. It performs all the steps necessary to do one calculation. It will orchestrate the reading of a line of input data, the matching pieces to either an interest or currency calculation, the dispatching of this data to the corresponding computational function, and error checking in several steps.

In particular DoCommand must call ReadLine with pointers set to the appropriate buffer (tbuf, for typing buffer). If no characters are read, it must call ReadLine again until some are. Then DoCommand must call various patterns of GetLetter and GetNumber to parse out a command.

The first item in the line must be either i, c, or q. If it reads q, then DoCommand must return a 0 so the main loop will know to quit. If instead it reads i, it must then read three fixed-point decimal numbers, the principle, the interest rate, and the number of periods, respectively. DoCommand can then call CalculateInterest and print the result.

Correspondingly, if the first letter is c, DoCommand must get two more letters, and a number. These are, in order, the originating currency, the resulting currency, and the amount in the original currency. For instance, c d c 100.30 represents a conversion from 100.30 US dollars to Canadian dollars.

For any other first letters, including of course any non-letters that cause GetLetter to return an error, DoCommand must report an error. The help message should be provided at this point in time.

If any of the GetNumber or GetLetter calls returns an error, or if the call to the computational function returns an error, DoCommand should display the error message (but no help message), and exit with a nonzero value in ax to indicate not to quit. Otherwise if no errors occur, DoCommand should print out the result (preceded by the result message, of course).

ReadLine

Before we can retrieve any values from the string which the user inputs, we must read the string into a buffer. There are several important details to this function, so read carefully!

  • Ignore linefeed characters (LF)

    LF characters are part of a file stored in the disk, generated as one of two characters for every newline (think Enter or Return) stored in the file. By ignoring this we can take files as test inputs.

  • Handle Backspace (BS)

    When the users make a mistake in typing, chances are they will hit the Backspace key. In general we want to make this delete the previous character, perhaps in memory just decreasing our length counter. To actually delete the character on the screen, however, we must display a backspace to move back, a space to remove the displayed character, and a second backspace to move back once again.

    Note that the backspace should result in a beep when pressed at the beginning of the buffer, and should work normally when pressed at the end of the buffer.

  • End input upon Enter (CR)

    When the user presses Enter, ReadLine must place a '$' at the spot where the CR would otherwise go, and return with the appropriate outputs. Enter should be accepted at any point along the line, including an empty or a full buffer.

  • Don't overflow the buffer

    One of your inputs is the length of your buffer. Don't write to more memory than this value indicates, as it can corrupt essential data. Chances are you won't trigger this problem in normal use, but expect the TA to check it. When the user presses a normal key (other than one of the above) and the buffer is full, ReadLine must beep and not change the buffer's contents.

  • Everything else

    Chances are anything else should just be copied into the buffer and echoed to the screen. If you find that you wish to improve the user experience by limiting user input to valid letters and numbers only, you may do so (it must still accept CR, BS, and LF as above, though).

Note the inputs and outputs; you're given a buffer address and size, and must return a (partially) filled buffer, and a count of filled characters. Prompt the user by displaying "" but do not place it in the buffer. Don't forget to write to the screen.

GetLetter

The purpose of GetLetter is simple. Ignoring any intervening spaces, it should return in al the ASCII value of the first letter at or after offset bx. If the first value which is not a space is not a letter, it should return ax=0 instead to indicate an error. GetLetter should also update bx to point to the character after the letter it returned.

GetNumber

GetNumber functions similarly to GetLetter. It receives as input the offset of (part of) a string, and returns in ax a value indicating whether it was successful. Instead of returning a successful letter in al, however, GetLetter places the read number in the memory located at offset di; di is an input to GetNumber.

CalculateInterest

Calculate the interest by calling appropriate FDx functions and manipulating pointers correctly. To be fully functional, CalculateInterest may not write to any of its inputs. If necessary, however, it may use other buffers such as the pre-declared numbuf1. Remember that the equation that we are using is:

[result] = [principle] * (1+[interest])[periods]

Note that large numbers, especially as the exponent, can cause overflows. You do not have to handle these; merely understand why the occur with large numbers

ConvertCurrency

Converting currency is probably a longer procedure than calculating interest, but mostly because it must validate its input. The two currencies chosen are represented with letters in dh and dl. You must make sure that the provided arguments are really letters because you must then use them as indices in the lookup-tables tab_from and tab_to. Furthermore, if the value you retrieve from the table is 0, it means the user has requested an invalid currency. Otherwise it is the address of a FDNumber as found online last week. The equation for currency conversion is:

[result] = from * to * [principle],

where from and to are the values looked up in tab_from and tab_to respectively.

Note: on any invalid input ConvertCurrency must signal error. Like CalculateInterest, ConvertCurrency should not write to any of its inputs, and instead should write to [result].

Subroutines

In this section there is a listing of the inputs, outputs, and summarized actions of each function you will need to write. Refer to the above information or to the Gradesheet for more information on what is required.

DoCommand

Inputs: none
Outputs:
ax = 1 to continue or 0 to quit
Purpose: gets and processes a command, reporting the result
Calls:
dspmsg, ReadLine, GetLetter, GetNumber, CalculateInterest, ConvertCurrency, FDWrite

ReadLine

Inputs:
ax = size of the buffer
bx = offset of the buffer
Outputs:
ax = number of characters in the buffer (excluding '$')
buffer at bx holds the typed characters
Purpose: take keyboard entry from the user, handling backspace and bounds
Calls:
dspout, kbdin, optionally dspmsg
Note:
Display the "" but do not place it in the buffer.

GetLetter

Inputs:
bx = offset of the buffer from which to read
Outputs:
al = ASCII value of first letter (ignoring spaces), or ax = 0 if it's not a letter
bx = offset of the character following the letter returned, undefined on error
Purpose: retrieve a letter from the input string; error if one isn't available

GetNumber

Inputs:
bx = offset of the buffer from which to read di = offset of the FDNumber in which to store the read number
Outputs:
ax = 0 on error or non-zero on success
bx = offset of the character following the number returned, undefined on error
[di] = the number read from the string
Purpose: retrieve a FDNumber from the input string; error if one isn't available
Calls:
FDRead

CalculateInterest

Inputs:
[principle] = the principle for the calculation
[interest] = the interest rate per period
[periods] = the number of periods to compound
All three of the above are FDNumbers
Outputs:
ax = 0 on error or non-zero on success
[result] = the result of the calculation principle * (1 + interest)periods
Purpose: calculate the compound interest, storing to [result]
Calls:
FDAdd, FDMul, FDPow
Note:
This subroutine should always succeed; ax should thus always be non-zero on exit

ConvertCurrency

Inputs:
[principle] = the principle for the calculation, a FDNumber
dh = the letter for the currency from which to convert
dl = the letter for the currency to which to convert
Outputs:
ax = 0 on error or non-zero on success
[result] = the result of the calculation principle * from * to
Purpose: calculate a currency conversion, storing to [result]
Calls:
FDMul
Note:
This subroutine can fail, so make sure to properly check for such error conditions

FDRead

Inputs:
bx = offset to a string representing a decimal number
di = offset of the buffer to which it should be stored
Outputs:
bx = offset of the first non-converted character
dl = conversion error code, as in ascbin; use overflow if the portion following the decimal is above 99
[di] holds the converted FDNumber
Purpose: input a FDNumber from a user-typed string
Calls:
ascbin
Note:
The formats of supported numbers are varied, and a number like 100.3 is tricky; see above

FDWrite

Inputs:
bx = offset to a 10 byte buffer
si = offset of the FDNumber to be converted into ASCII
Outputs:
bx = offset of the first non-blank character, with the number right-justified and with spaces to the left; two digits after the decimal point
cl = number of non-blank characters
Purpose: output a FDNumber into a user-readable string
Calls:
binasc
Note:
Numbers like 100.03 must have the zero inserted manually
you may wish to use a second buffer for at least one of the calls to binasc

FDAdd

Inputs:
si = offset to a source FDNumber
di = offset to a source and destination FDNumber
Outputs:
[di] = holds the result of the addition
Purpose: Adds the FDNumbers at offsets di, si and stores at offset di
Note:
When the decimal portion exceeds 99, the overflow must be transferred into the integer portion of the FDNumber

FDMul

Inputs:
si = offset to a source FDNumber
di = offset to a source and destination FDNumber
Outputs:
[di] = holds the result of the multiplication
Purpose: Multiplies the FDNumbers at offsets di, si and stores at offset di
Calls:
FDAdd, optionally
Note:
Round to the nearest hundredth by turning .005 and higher into .01
You may find it useful to use a scratch FDNumber or two

FDPow

Inputs:
si = offset to a source FDNumber as exponent
di = offset to a source FDNumber as base
Outputs:
dx = integer portion of the result
ax = the hundredths portion of the result
Purpose: raises [di] to the [si] power
Calls:
FDMul
Note:
Recursion takes practice
Ignore the non-integer portion of [si] if there is one

Hints

  • The libmp2 file contains executable library functions for each of the routines that you need to implement. This allows you to run the program and understand how it works before you implement it. You can test your program by stepping through it and making sure the display and comparison functions work correctly. You will only receive credit, however, for the routines that you implement yourself.
  • When debugging your code in Turbo Debugger, you will find it helpful to use the memory window to show you the memory location of the file and strings. You can also use the watch window to keep track of variables in your code.
  • Run the program many times to get a good feel of it.  Your final program with your code should resemble our version.  Formatting aesthetics aren't as important as correctly operating code.
  • Do not forget to push and pop to avoid clobbering registers!
  • Push and Pop can also be used on memory, as long as you specify the size and it's word-sized or larger. You don't have to move it into a register first. (Yes, this does appear to break the two-memory access rule. Don't worry!)
  • Remember to include function headers for and write comments in each of your functions!  Headers should give a general idea of what the function does and have a list of inputs, outputs, and called functions.  Commenting should be specific enough to allow a function to be understood by just reading the comments.  Commenting every line, however, is excessive and may cost you some points.
  • START EARLY!
  • Monitor the newsgroup for clarifications and help.

Procedure

  • You will begin this MP with the following files:
    • MP2.ASM: Program Framework
    • Makefile: Specifies how and when programs are assembled and linked.
    • libmp2.lib: Library functions for MP2
    • lib291.lib: General-purpose library functions
  • You may copy these files from the network drive to your home directory with the following command:
    xcopy /s V:\ece291\mp2 W:\mp2
    or download the files from this server as mp2.zip
  • Add your code to MP2.ASM.
  • Assemble and link your program by typing
    make
    This command reads Makefile then invokes NASM and LINK to build an executable program.
  • Use Turbo Debugger (TD) to find and correct program errors.

Final Steps

  1. Demonstrate your MP2.EXE to a TA. You may be asked to recompile and demo the program. Your program must work with all given input.
  2. Be prepared to answer questions about any aspect of the operation of your program. The TAs will not accept an MP if you cannot fully explain your code and your implementation. Delayed MPs will be subject to late penalties as described in the course syllabus (10% pts per day).
  3. The TA will complete the code submission procedure.

MP2.ASM (program framework)

; MP2 - Your Name - Today's Date
;
;
; Josh Potts, Fall 2001
; Guest Authors: Michael Urman, Justin Quek
; University of Illinois, Urbana-Champaign
; Dept. of Electrical and Computer Engineering
;
; Version 1.0

	BITS	16

;====== SECTION 1: Define constants =======================================

	BS	    EQU 8
        CR          EQU 0Dh
        LF          EQU 0Ah
        BEL         EQU 07h
	TBUF_SIZE   EQU 79

;====== SECTION 2: Declare external procedures ============================

; These are functions from lib291
EXTERN kbdin, dspout, dspmsg, ascbin, binasc, mp2xit

; You will be writing your own versions of these functions
EXTERN _libDoCommand, _libReadLine, _libGetLetter, _libGetNumber
EXTERN _libCalculateInterest, _libConvertCurrency
EXTERN _libFDRead, _libFDWrite, _libFDAdd, _libFDMul, _libFDPow

; The _lib functions need these to work properly
GLOBAL _DoCommand, _ReadLine, _GetLetter, _GetNumber
GLOBAL _CalculateInterest, _ConvertCurrency
GLOBAL _FDRead, _FDWrite, _FDAdd, _FDMul, _FDPow
GLOBAL tbuf, pbuf, binascbuf
GLOBAL principle, interest, periods, result
GLOBAL numbuf1, numbuf2
GLOBAL msg_help, msg_error, msg_result, msg_crlf
GLOBAL tab_from, tab_to

;====== SECTION 3: Define stack segment ===================================

SEGMENT stkseg STACK                    ; *** STACK SEGMENT ***
        resb      64*8
stacktop:
	resb 0

;====== SECTION 4: Define code segment ====================================

SEGMENT code                            ; *** CODE SEGMENT ***

;====== SECTION 5: Declare variables for main procedure ===================
tbuf		resb TBUF_SIZE
pbuf            resb 10
binascbuf	resb 7
principle	resd 1
interest	resd 1
periods		resd 1
result		resd 1
numbuf1		resd 1
numbuf2		resd 1

msg_help	db CR, LF, 'Perform an Interest calculation by entering'
		db CR, LF, '  i   '
		db CR, LF
		db CR, LF, 'Perform a currency conversion by entering'
		db CR, LF, '  c   '
		db CR, LF, ' where  and  are one of the letters:'
		db CR, LF, '  c d e f g k l* m p q r s w* y z'
		db CR, LF, ' Letters with a star can only be .'
		db CR, LF, '$'

msg_error	db CR, LF, ' ** ERROR **', CR, LF, '$'
msg_result	db CR, LF, ' The result is: ', '$'
msg_crlf	db CR, LF, '$'

; c = canada dollar
; d = us dollar
; e = euros
; f = france francs
; g = germany deutchmarks
; k = czech republic koruny
; l = italy lira
; m = mexico peso
; p = great britain pounds
; r = russia rubles
; s = israel new shekel
; w = south korea won
; y = japan yen

tab_from	dw 0, 0			; a, b
		dw from_canadian	; c
		dw from_dollar		; d
		dw from_euros		; e
		dw from_francs		; f
		dw from_deutsche	; g
		dw 0, 0, 0		; h, i, j
		dw from_koruny		; k
		dw 0			; l
		dw from_peso		; m 
		dw 0, 0			; n, o
		dw from_pound		; p
		dw 0			; q
		dw from_ruble		; r
		dw from_shekel		; s
		dw 0, 0, 0, 0, 0	; t, u, v, w, x
		dw from_yen		; y
		dw 0			; z
		
tab_to		dw 0, 0			; a, b
		dw to_canadian		; c
		dw to_dollar		; d
		dw to_euros		; e
		dw to_francs		; f
		dw to_deutsche		; g
		dw 0, 0, 0		; h, i, j
		dw to_koruny		; k
		dw to_lira		; l
		dw to_peso		; m 
		dw 0, 0			; n, o
		dw to_pound		; p
		dw 0			; q
		dw to_ruble		; r
		dw to_shekel		; s
		dw 0, 0, 0		; t, u, v
		dw to_won		; w
		dw 0			; x
		dw to_yen		; y
		dw 0			; z

from_canadian	dw	0, 64
from_dollar	dw	1, 0
from_euros	dw	0, 91
from_francs	dw	0, 14
from_deutsche	dw	0, 46
from_koruny	dw	0, 03
from_peso	dw	0, 11
from_pound	dw	1, 46
from_ruble	dw	0, 03
from_shekel	dw	0, 23
from_yen	dw	0, 01

to_canadian	dw	1, 56
to_dollar	dw	1, 0
to_euros	dw	1, 10
to_francs	dw	7, 23
to_deutsche	dw	2, 16
to_koruny	dw	37, 61
to_lira		dw	2133, 87
to_peso		dw	9, 29
to_pound	dw	0, 68
to_ruble	dw	29, 2
to_shekel	dw	4, 29
to_won		dw	1281, 0
to_yen		dw	120, 0


;====== SECTION 6: Program initialization =================================

..start:
        mov     ax, cs                  ; Initialize Default Segment register
        mov     ds, ax  
        mov     ax, stkseg              ; Initialize Stack Segment register
        mov     ss, ax
        mov     sp, stacktop            ; Initialize Stack Pointer register

;====== SECTION 7: Main procedure =========================================

MAIN:
	call	_DoCommand
	test	ax, ax
	jnz	MAIN
	
	call	mp2xit

;--------------------------------------------------------------------------
; DoCommand
;--------------------------------------------------------------------------
_DoCommand
	call	_libDoCommand
	ret

;--------------------------------------------------------------------------
; ReadLine
;--------------------------------------------------------------------------
_ReadLine
	call	_libReadLine
	ret

;--------------------------------------------------------------------------
; GetLetter
;--------------------------------------------------------------------------
_GetLetter
	call	_libGetLetter
	ret

;--------------------------------------------------------------------------
; GetNumber
;--------------------------------------------------------------------------
_GetNumber
	call	_libGetNumber
	ret

;--------------------------------------------------------------------------
; CalculateInterest
;--------------------------------------------------------------------------
_CalculateInterest
	call	_libCalculateInterest
	ret

;--------------------------------------------------------------------------
; ConvertCurrency
;--------------------------------------------------------------------------
_ConvertCurrency
	call	_libConvertCurrency
	ret

;--------------------------------------------------------------------------
; FDRead
;--------------------------------------------------------------------------
_FDRead
	call	_libFDRead
	ret

;--------------------------------------------------------------------------
; FDWrite
;--------------------------------------------------------------------------
_FDWrite
	call	_libFDWrite
	ret

;--------------------------------------------------------------------------
; FDAdd
;--------------------------------------------------------------------------
_FDAdd
	call	_libFDAdd
	ret

;--------------------------------------------------------------------------
; FDMul
;--------------------------------------------------------------------------
_FDMul
	call	_libFDMul
	ret

;--------------------------------------------------------------------------
; FDPow
;--------------------------------------------------------------------------
_FDPow
	call	_libFDPow
	ret
Return to ECE291 Home Page Fall 2001