CIS-77 Home http://www.c-jump.com/CIS77/CIS77syllabus.htm
Download sample program add_16_bytes.asm ( download ).
Current version of the program is summing an array of 16 bytes. The steps are:
Load starting address of the array into ESI.
Loop for each element in the array.
Get the value using the offset in ESI via indirect memory addressing.
Add the value to the running total in register AL.
Update the offset in ESI to point to the next element of the array.
Update loop counter.
If counter is not zero, repeat from step 4.
; CIS-77 ; add_16_bytes.asm ; .586P .MODEL FLAT ; Flat memory model option casemap:none ; Treat labels as case-sensitive .CONST ; Constant data segment .STACK 100h ; (default is 1-kilobyte stack) .DATA ; Begin initialized data segment values db 16 DUP( 5 ) ; 16 bytes of values "5" ; Code segment .CODE ; Begin code segment _main PROC ; Beginning of code mov eax, 0 ; clear result mov bl, 16 ; init loop counter lea esi, values ; init data pointer addup: add al, [esi] ; add byte to sum inc esi ; increment data pointer dec bl ; decrement loop counter jnz addup ; if BL not zero, continue mov [esi], al ; save sum ret ; Exit _main ENDP END _main ; Marks the end of the module and sets the program entry point label
Command
dumpbin /exports C:\Windows\System32\kernel32.dll
prints a list of windows API functions available to a console application.
One of the APIs is a Beep function, which generates simple tone on the system speaker (not using the sound card.)
The following C++ program demonstrates a loop of 50-millisecond Beeps with rising the tone frequency:
#include <iostream> #include <windows.h> int main() { int ms = 50; // sound duration in milliseconds for ( int frequency = 100; frequency < 1000; frequency += 10 ) { std::cout << frequency << " "; // display sound frequency in hertz before each tone. Beep( frequency, ms ); } return 0; }
Most kernel32 API calls return result in EAX reqister.
If Beep function succeeds, the return value is nonzero.
If the function fails, the return value is zero. To get extended error information, application can call GetLastError API:
// C example: DWORD result = Beep( frequency, ms ); if ( result == 0 ) { DWORD error_code = GetLastError(); }
The following program, M09.BAT ( download ) demonstrates Beep API call, made from an assembly program:
; @goto masm ; CIS-77 Lab exercise M09 ; M09.BAT ; Program that beeps ; .386 ; Tells MASM to use Intel 80386 instruction set. .MODEL FLAT ; Flat memory model option casemap:none ; Treat labels as case-sensitive EXTERN _Beep@8:NEAR .CONST ; Constant data segment .DATA ; Begin initialised data segment .CODE ; Begin code segment _main PROC ; Main entry point into program ; __stdcall calling convention: args pushed R to L mov eax, 200 ; duration, milliseconds push eax mov eax, 1000 ; frequency, Hertz push eax call _Beep@8 ; Beep( frequency, duration ); ret _main ENDP END _main ; Marks the end of the module and sets the program entry point label :masm ; @call "C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\vsvars32.bat" ; @echo ___________________________________________________ ; ml /c /coff M09.BAT ; @echo ___________________________________________________ ; link /subsystem:console /entry:main /out:M09.exe M09.OBJ kernel32.lib ; @echo ___________________________________________________ ; @pause
The EXTERN MASM directive,
EXTERN _Beep@8:NEAR
defines a program symbol _Beep@8 with a NEAR type, which is needed by the assembler to generate the appropriate CALL instruction.
The linker also gets the kernel32.lib object library on command line:
link /subsystem:console /entry:main /out:M09.exe M09.OBJ kernel32.lib
The library provides the actual binary definition of the Beep system function.
Almost all Windows system functions use __stdcall calling convention . The system function removes its own arguments from the stack.
The __stdcall convention also requires a
function name prefixed by _underscore, and
appended function name suffix @, followed by the decimal number of bytes in the argument list. For example,
EXTERN _Beep@8:NEAR ; Function uses two DWORD arguments EXTERN _GetLastError@0:NEAR ; Function has no arguments .. call _Beep@8 call _GetLastError@0
Under __stdcall convention, function arguments are passed on the stack in right to left order. Download cjumpcxx.exe program, and click
Functions -> Stack Frames
to view animation of the function call.
Please note that M09.BAT demo program is a hack. It pretends to be both Windows batch command file and an assembly program at the same time:
The file has .BAT file extension instead of a typical .ASM
The code begins with a "goto" statement that directs batch command interpreter to the label ":masm". Batch interpreter is oblivious to the semicolons that are comment symbols for the Assembler.
Assembly program follows.
Assembly does not care about any text after the "END" directive.
The rest of the batch commands follow the END.
Now you can double click on the M09.EXE file to compile the M09.EXE program:
The program is assembled and linked in the same directory where the M09.BAT is located.
I am using this hack in the classroom because I need to assemble the demo code as quickly as possible.
Please note that the Beep call is not portable and is largely dependent on the OS that we are using.
The closest "beep" behavior that you can get in standard a C/C++ is to send the '\a' escape sequence to the standard output, that is, either
to the stdout stream,
printf( "\a" );
or to the std::cout object, for example,
std::cout << "\a";
The C++ standard inherits this from C standard:
"\a (alert) produces an audible or visible alert without changing the active cursor position."
On most common desktop systems, '\a' produces a beep from the system speaker, assuming the computer has one.
Modify the original add_16_bytes.asm program as follows:
Change the type of "values" array to a 32-bit doubleword.
While making changes,
Every CPU instruction in your program must have a brief comment.
Use .CONST memory segment to define necessary constants.
Use LENGTHOF and TYPE operators to refer to the number of elements in the array and size of each array element, respectively.
Replace LEA instructions by MOV instructions with OFFSET operators.
Add console I/O to prompt for the user input, and then have the user input a certain number of values.
Instead of calculating the sum, write assembly code to generate tones on the system speaker by calling Beep. Interpret array of integers as pairs of duration and frequency values.
Note : the Beep function modifies all general purpose registers EAX, EDX, ECX, and so on. If you rely on values in those registers to control your loops, memory addresses, offsets, etc., etc., please be sure to preserve your registers before the Beep call and restore the registers upon return from the call. One way to preserve the values is to push them on the stack, and restore them by popping in the reversed order. However, this needs to happen before duration and frequency parameters are pushed on the stack. Alternatively, you can have dedicated memory locations to temporarily preserve your registers.
Add logic to handle potential Beep error codes:
If the function fails, the return value in EAX register is zero and no additional action is required.
If the Beep function succeeds, the return value in EAX is nonzero. In that case, to get extended error information, call GetLastError API function.
The GetLastError returns the error code in EAX. Program should print this error code on the screen.
As the sound is produced, display the tone frequency and duration on the console screen.
Ask the user if they wish to retry, and if so, start over. Exit the program otherwise.
Optional (15 xtra pts.) The original size of "values" array is 16 elements. However, the program could ask the user how many sample values they wish to enter.
Optional (5 xtra pts.) Modify program to use the at least one LOOP instruction.
Optional (10 xtra pts.) Modify program and add a 200-millisecond delay before each beep. (Hint: lookup kernel32 Sleep API documentation.)
What to submit:
Submit only your ASM source file (not the EXE or project.)