Materials:![]() ![]() ![]() ![]() ![]() Objectives: ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Competency: The student will learn how to design executable assembly lanugage programs and how to compile and link the source code text file into an executable. The student will become familiar with some basic compiler "dot" directives and some simple machine language instructions. |
Preparation
The student will need a copy of Borland's Turbo Assembly Lanugage software, preferably version 5.0.
After launching QBOOT by pressing [F8] say yes to all CONFIG.SYS prompts and all AUTOEXEC.BAT prompts except the last one to launch Windows 3.11.
Change to the K: drive and insert the source code diskette created in the Introduction to TASM module. Copy BLANK.ASM from the diskette to the root of the K: drive and then run "edit blank.asm" All assembly language source files should have the file extention .ASM so that the compilers can easily recognize them.
Procedures
In EDIT's main field, the following source code should be present:
.model tiny .stack 200h .data .code main proc mov ax, @data mov ds, ax terminate: mov ax, 4C00h int 21h main endp end main
Press [Alt]+[F] to open the File menu and then press [A] to save the file with a new name. Name the file GUESS.ASM. Make the following changes:
.model tiny .stack 200h .data titlemsg db "Guess the Number Version 1.0", 0Dh, 0Ah, '$' playmsg db "Play the game? (Y/N): $" promptmsg db 0Dh, 0Ah, "Guess a number between 1 and 100 (Q=Quit): $" toohimsg db 0Dh, 0Ah, "Too High!", 0Dh, 0Ah, '$' toolomsg db 0Dh, 0Ah, "Too Low!", 0Dh, 0Ah, '$' gotitmsg db 0Dh, 0Ah, "You got it!", 0Dh, 0Ah, '$' badinmsg db 0Dh, 0Ah, "Entry must be number from 1-100 or Q!", 0Dh, 0Ah, '$' maxlen db 4 strlen db 0 inbuf db 5 dup('$') randnum db 0 usernum db 0 placevals db 1, 10, 100 .code main proc mov ax, @data mov ds, ax start: mov ah, 9 mov dx, offset titlemsg int 21h again: mov ah, 9 mov dx, offset playmsg int 21h mov ah, 1 ;function 1: input one character and wait for it int 21h cmp al, 'y' ;function 1 returns the char in AL je cont1 cmp al, 'Y' ;checking for either upper or lower case Y je cont1 jmp terminate ;automatically quit with any other response cont1: ;generate random number here cont2: ;get a guess from the user here ;compare it with the random number here ;display correct response, if they guessed it then ;jump to "again" else jump to cont2 terminate: mov ax, 4C00h ;function 4C = quit program int 21h main endp end main
Press [Alt]+[F] then [S] to save the changes. The highlighted lines are the ones that were added to the original BLANK.ASM. The first change creates the new variables and the second change starts creating the executable code. This is going to be a long project so it should be developed in stages starting with a few executable lines and then notes on what needs to be added later.
At this point because there is going to be a lot of repitition of commands to copy the file to the floppy, then compile, link and execute it a batch file can be developed to do these things automatically:
K:\>copy con dev.bat
@echo off
:repeat
edit %1.asm
choice /C:YN Continue
if errorlevel 2 goto end
copy %1.asm b:
choice /C:YN Continue
if errorlevel 2 goto end
tasm /zi %1
choice /C:YN Continue
if errorlevel 2 goto end
tlink /v %1
choice /C:YN Execute
if errorlevel 2 goto end
%1
choice /C:YN Continue
if errorlevel 2 goto end
goto repeat
:end
^Z
1 file(s) copied
K:\>_
Now copy dev.bat to the source code floppy then execute it like this:
K:\>copy dev.bat b: 1 file(s) copied K:\>dev guess
This will launch EDIT and load the source file on screen. Exit EDIT and the prompt to continue will be displayed. Type [Y] and the file will be copied to the floppy followed by another prompt to continue:
K:\>dev guess Continue[Y/N]?Y 1 file(s) copied Continue[Y/N]?_
Press [Y] and the batch file will execute the command to compile the source code file and follow this with another continue prompt:
K:\>dev guess Continue[Y/N]?Y 1 file(s) copied Continue[Y/N]?Y Turbo Assembler Version 4.1 Copyright (c) 1988, 1996 Borland International Assembling file: guess.ASM Error messages: None Warning messages: None Passes: 1 Remaining memory: 335k Continue[Y/N]?_
If the compiler reports no warnings or errors then press [Y] and it will then link the executable and follow this with an execute prompt:
K:\>dev guess Continue[Y/N]?Y 1 file(s) copied Continue[Y/N]?Y Turbo Assembler Version 4.1 Copyright (c) 1988, 1996 Borland International Assembling file: guess.ASM Error messages: None Warning messages: None Passes: 1 Remaining memory: 335k Continue[Y/N]?Y Turbo Link Version 7.1.30.1. Copyright (c) 1987, 1996 Borland International Execute[Y/N]?_
This one is dangerous because the executable may have logic bugs that cause it to lock up the machine. In the future the DEV.BAT will be modified to allow us to either run the program that is being compiled and linked OR to send it to the Turbo Debugger. For now press [Y] and the program runs:
K:\>dev guess Continue[Y/N]?Y 1 file(s) copied Continue[Y/N]?Y Turbo Assembler Version 4.1 Copyright (c) 1988, 1996 Borland International Assembling file: guess.ASM Error messages: None Warning messages: None Passes: 1 Remaining memory: 335k Continue[Y/N]?Y Turbo Link Version 7.1.30.1. Copyright (c) 1987, 1996 Borland International Execute[Y/N]?Y Guess the Number Version 1.0 Play the game? (Y/N): _
Press [N] and the program should end and display the continue prompt again:
K:\>dev guess Continue[Y/N]?Y 1 file(s) copied Continue[Y/N]?Y Turbo Assembler Version 4.1 Copyright (c) 1988, 1996 Borland International Assembling file: guess.ASM Error messages: None Warning messages: None Passes: 1 Remaining memory: 335k Continue[Y/N]?Y Turbo Link Version 7.1.30.1. Copyright (c) 1987, 1996 Borland International Execute[Y/N]?Y Guess the Number Version 1.0 Play the game? (Y/N): n Continue[Y/N]?_
Pressing [Y] will loop the batch file back to the repeat label and open the source code in EDIT once again. Pressing [N] will quit the batch file and land at the command prompt again. Press [Y] to open the program in EDIT once again. Make the following changes:
.model tiny .stack 200h .data titlemsg db "Guess the Number Version 1.0", 0Dh, 0Ah, '$' playmsg db "Play the game? (Y/N): $" promptmsg db 0Dh, 0Ah, "Guess a number between 1 and 100 (Q=Quit): $" toohimsg db 0Dh, 0Ah, "Too High!", 0Dh, 0Ah, '$' toolomsg db 0Dh, 0Ah, "Too Low!", 0Dh, 0Ah, '$' gotitmsg db 0Dh, 0Ah, "You got it!", 0Dh, 0Ah, '$' badinmsg db 0Dh, 0Ah, "Entry must be number from 1-100 or Q!", 0Dh, 0Ah, '$' maxlen db 4 strlen db 0 inbuf db 5 dup('$') randnum db 0 usernum db 0 placevals db 1, 10, 100 .code main proc mov ax, @data mov ds, ax start: mov ah, 9 mov dx, offset titlemsg int 21h again: mov ah, 9 mov dx, offset playmsg int 21h mov ah, 1 ;function 1: input one character and wait for it int 21h cmp al, 'y' ;function 1 returns the char in AL je cont1 cmp al, 'Y' ;checking for either upper or lower case Y je cont1 jmp terminate ;automatically quit with any other response cont1: call getrand ;generate random number cont2: ;get a guess from the user here ;compare it with the random number here ;display correct response, if they guessed it then ;jump to "again" else jump to cont2 terminate: mov ax, 4C00h ;function 4C = quit program int 21h main endp getrand proc mov ah, 2Ch ;function 2Ch: get system time from BIOS int 21h ;returns CH=hour, CL=minute, DH=second, DL=1/100ths of second ;it is the hundredths of a second that will help generate a nice random ;number since it is nearly impossible to predict xor ax, ax ;AX=0 add al, ch ;add hour add al, cl ;add minute add al, dh ;add second to it add al, dl ;add hundredths of second to it ;cl now holds hour + minute + second + hundredths of a second mov cx, 100 ;put the value 100 in ax for the DIV instruction div cl ;the DIV instruction assumes that the named register or variable ;will be divided into the number in the AL register ;the result will be held in the AL and the remainder ;will be left in the AH inc ah ;remainders of dividing anything by 100 will be zero to 99 ;add 1 and the possible numbers that will be left in AH are 1 to 100 mov randnum, ah ;store the random number that can be from 1 to 100 into the variable ret ;return from the procedure getrand endp end main
Save the changes and exit EDIT. At the continue prompt enter a [Y] to copy the file to the floppy. Enter another [Y] to compile the source code. Be sure there are no errors. Press [Y] again to link the object file. Again be sure there are no errors. Press [N] when prompted to execute since the random value is stored in RAM and never displayed. Run dev guess again to open the source in EDIT again. Make the following changes:
.model tiny .stack 200h .data titlemsg db "Guess the Number Version 1.0", 0Dh, 0Ah, '$' playmsg db "Play the game? (Y/N): $" promptmsg db 0Dh, 0Ah, "Guess a number between 1 and 100 (Q=Quit): $" toohimsg db 0Dh, 0Ah, "Too High!", 0Dh, 0Ah, '$' toolomsg db 0Dh, 0Ah, "Too Low!", 0Dh, 0Ah, '$' gotitmsg db 0Dh, 0Ah, "You got it!", 0Dh, 0Ah, '$' badinmsg db 0Dh, 0Ah, "Entry must be number from 1-100 or Q!", 0Dh, 0Ah, '$' maxlen db 4 strlen db 0 inbuf db 5 dup('$') randnum db 0 usernum db 0 placevals db 1, 10, 100 .code main proc mov ax, @data mov ds, ax start: mov ah, 9 mov dx, offset titlemsg int 21h again: mov ah, 9 mov dx, offset playmsg int 21h mov ah, 1 ;function 1: input one character and wait for it int 21h cmp al, 'y' ;function 1 returns the char in AL je cont1 cmp al, 'Y' ;checking for either upper or lower case Y je cont1 jmp terminate ;automatically quit with any other response cont1: call getrand ;generate random number cont2: call getguess ;get the guess call asc2bin ;convert it to binary jc cont2 ;if conversion fails asc2bin will display a msg and set the carry flag ;compare it with the random number here ;display correct response, if they guessed it then ;jump to "again" else jump to cont2 terminate: mov ax, 4C00h ;function 4C = quit program int 21h main endp getrand proc mov ah, 2Ch ;function 2Ch: get system time from BIOS int 21h ;returns CH=hour, CL=minute, DH=second, DL=1/100ths of second ;it is the hundredths of a second that will help generate a nice random ;number since it is nearly impossible to predict xor ax, ax ;AX=0 add al, ch ;add hour add al, cl ;add minute add al, dh ;add second to it add al, dl ;add hundredths of second to it ;cl now holds hour + minute + second + hundredths of a second mov cx, 100 ;put the value 100 in ax for the DIV instruction div cl ;the DIV instruction assumes that the named register or variable ;will be divided into the number in the AL register ;the result will be held in the AL and the remainder ;will be left in the AH inc ah ;remainders of dividing anything by 100 will be zero to 99 ;add 1 and the possible numbers that will be left in AH are 1 to 100 mov randnum, ah ;store the random number that can be from 1 to 100 into the variable ret ;return from the procedure getrand endp getguess proc mov ah, 9 mov dx, offset promptmsg int 21h mov ah, 0Ah mov dx, offset maxlen int 21h ret getguess endp asc2bin proc xor ax, ax ;AX=0 mov usernum, al ;clear unser number xor si, si ;SI=0 xor cx, cx ;CX=0 mov cl, strlen ;get the size of the string that the user entered mov bx, offset strlen ;place offset of variable into BX mov di, offset placevals ;place offset of place values into DI a2b1: xor dx, dx ;DX=0 mov si, cx ;place offset of digit in buffer into SI mov dl, [bx + si] ;place ASCII digit into DL sub dl, 30h ;subtract 30h leaving the binary equivalent of the digit mov al, [di] ;get place value into AL mul dl ;multiply dl x al result in AX add usernum, al ;move the value into variable inc di ;point to next place value loop a2b1 ;dec 1 from cx, if cx=0 fall out of loop otherwize jmp to label mov al, usernum ;retrieve usernum cmp al, 100 ;is it larger than 100? ja a2b2 ;yes jump to the error handler, otherwise ... clc ;clear the carry flag, jmp a2bexit ;and exit the proc a2b2: mov ah, 9 mov dx, offset badinmsg int 21h stc ;show error message and set the carry flag a2bexit: ret asc2bin endp end main
Save these changes and exit EDIT. Press [Y] to continue which copies the file to the diskette, the next [Y] compiles, check for error messages. The next [Y] links, again check for errors. Press [N] to avoid executing the program then run dev guess again and make these changes:
.model tiny .stack 200h .data titlemsg db "Guess the Number Version 1.0", 0Dh, 0Ah, '$' playmsg db "Play the game? (Y/N): $" promptmsg db 0Dh, 0Ah, "Guess a number between 1 and 100 (Q=Quit): $" toohimsg db 0Dh, 0Ah, "Too High!", 0Dh, 0Ah, '$' toolomsg db 0Dh, 0Ah, "Too Low!", 0Dh, 0Ah, '$' gotitmsg db 0Dh, 0Ah, "You got it!", 0Dh, 0Ah, '$' badinmsg db 0Dh, 0Ah, "Entry must be number from 1-100 or Q!", 0Dh, 0Ah, '$' maxlen db 4 strlen db 0 inbuf db 5 dup('$') randnum db 0 usernum db 0 placevals db 1, 10, 100 .code main proc mov ax, @data mov ds, ax start: mov ah, 9 mov dx, offset titlemsg int 21h again: mov ah, 9 mov dx, offset playmsg int 21h mov ah, 1 ;function 1: input one character and wait for it int 21h cmp al, 'y' ;function 1 returns the char in AL je cont1 cmp al, 'Y' ;checking for either upper or lower case Y je cont1 jmp terminate ;automatically quit with any other response cont1: call getrand ;generate random number cont2: call getguess ;get the guess call asc2bin ;convert it to binary jc cont2 ;if conversion fails asc2bin will display a msg and set the carry flag call chkguess ;compare it with the random number here jc cont2 ;if guess incorrect get another guess jmp again ;if they guessed right ask to play again terminate: mov ax, 4C00h ;function 4C = quit program int 21h main endp getrand proc mov ah, 2Ch ;function 2Ch: get system time from BIOS int 21h ;returns CH=hour, CL=minute, DH=second, DL=1/100ths of second ;it is the hundredths of a second that will help generate a nice random ;number since it is nearly impossible to predict xor ax, ax ;AX=0 add al, ch ;add hour add al, cl ;add minute add al, dh ;add second to it add al, dl ;add hundredths of second to it ;cl now holds hour + minute + second + hundredths of a second mov cx, 100 ;put the value 100 in ax for the DIV instruction div cl ;the DIV instruction assumes that the named register or variable ;will be divided into the number in the AL register ;the result will be held in the AL and the remainder ;will be left in the AH inc ah ;remainders of dividing anything by 100 will be zero to 99 ;add 1 and the possible numbers that will be left in AH are 1 to 100 mov randnum, ah ;store the random number that can be from 1 to 100 into the variable ret ;return from the procedure getrand endp getguess proc mov ah, 9 mov dx, offset promptmsg int 21h mov ah, 0Ah mov dx, offset maxlen int 21h ret getguess endp asc2bin proc xor ax, ax ;AX=0 mov usernum, al ;clear unser number xor si, si ;SI=0 xor cx, cx ;CX=0 mov cl, strlen ;get the size of the string that the user entered mov bx, offset strlen ;place offset of variable into BX mov di, offset placevals ;place offset of place values into DI a2b1: xor dx, dx ;DX=0 mov si, cx ;place offset of digit in buffer into SI mov dl, [bx + si] ;place ASCII digit into DL sub dl, 30h ;subtract 30h leaving the binary equivalent of the digit mov al, [di] ;get place value into AL mul dl ;multiply dl x al result in AX add usernum, al ;move the value into variable inc di ;point to next place value loop a2b1 ;dec 1 from cx, if cx=0 fall out of loop otherwize jmp to label mov al, usernum ;retrieve usernum cmp al, 100 ;is it larger than 100? ja a2b2 ;yes jump to the error handler, otherwise ... clc ;clear the carry flag, jmp a2bexit ;and exit the proc a2b2: mov ah, 9 mov dx, offset badinmsg int 21h stc ;show error message and set the carry flag a2bexit: ret asc2bin endp chkguess proc mov al, randnum ;get the random number that the program generated earlier cmp usernum, al ;compare it with the user's guess jb toolow ;jb=JUMP if BELOW ja toohigh ;JA=JUMP if ABOVE mov ah, 9 ;if here then it must be equal (correct guess) mov dx, offset gotitmsg int 21h clc ;clear carry jmp chkgexit toolow: mov ah, 9 ;if here then it must be too low mov dx, offset toolomsg int 21h stc ;set carry jmp chkgexit toohigh: mov ah, 9 ;if here then it must be too high mov dx, offset toohimsg int 21h stc ;set carry chkgexit: ret chkguess endp end main
Save the changes and exit EDIT. Press [Y] to copy it to the floppy, [Y] to compile, [Y] to link and [N] to execute. At this point the game does work, but it does not check for the user entry of the letter "Q" as a number guess in order to quit and in fact does not check the user entry for bad data like letters and cahracters other than numbers or a "Q" at all. Run dev guess and make these changes:
.model tiny .stack 200h .data titlemsg db "Guess the Number Version 1.0", 0Dh, 0Ah, '$' playmsg db "Play the game? (Y/N): $" promptmsg db 0Dh, 0Ah, "Guess a number between 1 and 100 (Q=Quit): $" toohimsg db 0Dh, 0Ah, "Too High!", 0Dh, 0Ah, '$' toolomsg db 0Dh, 0Ah, "Too Low!", 0Dh, 0Ah, '$' gotitmsg db 0Dh, 0Ah, "You got it!", 0Dh, 0Ah, '$' badinmsg db 0Dh, 0Ah, "Entry must be number from 1-100 or Q!", 0Dh, 0Ah, '$' maxlen db 4 strlen db 0 inbuf db 5 dup('$') randnum db 0 usernum db 0 placevals db 1, 10, 100 quitflag db 0 .code main proc mov ax, @data mov ds, ax start: mov ah, 9 mov dx, offset titlemsg int 21h again: mov ah, 9 mov dx, offset playmsg int 21h mov ah, 1 ;function 1: input one character and wait for it int 21h cmp al, 'y' ;function 1 returns the char in AL je cont1 cmp al, 'Y' ;checking for either upper or lower case Y je cont1 jmp terminate ;automatically quit with any other response cont1: call getrand ;generate random number cont2: call getguess ;get the guess call asc2bin ;convert it to binary jc cont2 ;if conversion fails asc2bin will display a msg and set the carry flag cmp quitflag, 1 ;is quit flag set? je terminate ;yes then quit program call chkguess ;compare it with the random number here jc cont2 ;if guess incorrect get another guess jmp again ;if they guessed right ask to play again terminate: mov ax, 4C00h ;function 4C = quit program int 21h main endp getrand proc mov ah, 2Ch ;function 2Ch: get system time from BIOS int 21h ;returns CH=hour, CL=minute, DH=second, DL=1/100ths of second ;it is the hundredths of a second that will help generate a nice random ;number since it is nearly impossible to predict xor ax, ax ;AX=0 add al, ch ;add hour add al, cl ;add minute add al, dh ;add second to it add al, dl ;add hundredths of second to it ;cl now holds hour + minute + second + hundredths of a second mov cx, 100 ;put the value 100 in ax for the DIV instruction div cl ;the DIV instruction assumes that the named register or variable ;will be divided into the number in the AL register ;the result will be held in the AL and the remainder ;will be left in the AH inc ah ;remainders of dividing anything by 100 will be zero to 99 ;add 1 and the possible numbers that will be left in AH are 1 to 100 mov randnum, ah ;store the random number that can be from 1 to 100 into the variable ret ;return from the procedure getrand endp getguess proc mov ah, 9 mov dx, offset promptmsg int 21h mov ah, 0Ah mov dx, offset maxlen int 21h ret getguess endp asc2bin proc xor ax, ax ;AX=0 mov usernum, al ;clear user number xor si, si ;SI=0 xor cx, cx ;CX=0 mov cl, strlen ;get the size of the string that the user entered mov bx, offset strlen ;place offset of variable into BX mov di, offset placevals ;place offset of place values into DI a2b1: xor dx, dx ;DX=0 mov si, cx ;place offset of digit in buffer into SI mov dl, [bx + si] ;place ASCII digit into DL cmp dl, 30h ;is it a valid digit? jb a2b2 ;less than 30h is always disallowed cmp dl, 39h ja a2b3 ;above 39h could be a "Q" a2b4: sub dl, 30h ;subtract 30h leaving the binary equivalent of the digit mov al, [di] ;get place value into AL mul dl ;multiply dl x al result in AX add usernum, al ;move the value into variable inc di ;point to next place value loop a2b1 ;dec 1 from cx, if cx=0 fall out of loop otherwize jmp to label mov al, usernum ;retrieve usernum cmp al, 100 ;is it larger than 100? ja a2b2 ;yes jump to the error handler, otherwise ... clc ;clear the carry flag, jmp a2bexit ;and exit the proc a2b2: mov ah, 9 mov dx, offset badinmsg int 21h stc ;show error message and set the carry flag jmp a2bexit a2b3: cmp dl, 'q' ;is it a "Q"? je a2b5 cmp dl, 'Q' ;either upper or lower case is acceptable je a2b5 jmp a2b2 ;if it is not a "Q" then it is no good a2b5: clc ;clear carry inc quitflag ;set quitflag=1 and fall through to the end of the proc a2bexit: ret asc2bin endp chkguess proc mov al, randnum ;get the random number that the program generated earlier cmp usernum, al ;compare it with the user's guess jb toolow ;jb=JUMP if BELOW ja toohigh ;JA=JUMP if ABOVE mov ah, 9 ;if here then it must be equal (correct guess) mov dx, offset gotitmsg int 21h clc ;clear carry jmp chkgexit toolow: mov ah, 9 ;if here then it must be too low mov dx, offset toolomsg int 21h stc ;set carry jmp chkgexit toohigh: mov ah, 9 ;if here then it must be too high mov dx, offset toohimsg int 21h stc ;set carry chkgexit: ret chkguess endp end main
Save the changes and exit EDIT. [Y] to copy, [Y] to compile, [Y] to link and [N] to execute. The game is complete and functional at this point. Run it by typing "guess" then [Enter] at the DOS prompt. Play it a few times. Try bad entries for the number guesses. Try "Q" to see if it really quits.
This concludes the simple execution of a program that can gather input from a user and convert this input to the equivalent binary number and compare it with a randomly generated number. The next program will be able to use passed parameters from the command line.
Copyright©2000-2006 Brian Robinson ALL RIGHTS RESERVED