Using masm(Visual Studio 2010), write a program that inputs an entire line of text, up to 132 characters, into a buffer. Then have my program determine how many times each letter of the alphabet occurs. You are to count all letter a-z, A-Z, and 0-9 only. The screen should be out put a - 4 times, b - 2 times, c- 9 times.
问题:
回答1:
I made your program with compiler EMU8086 Intel syntax (just copy-paste-run). It's big but fully commented to help you understand, this is what it does: display a message for user, capture a string from keyboard (max 132), count digits and letters (other are ignored), display counters. Here it is (more explanations after the code):
.stack 100h
.data
msj1 db 'Enter a line of text (132 max) : $'
text db 133 ;MAX LENGTH 132, PLUS 1 FOR ENDING CHR(13).
db ? ;LENGTH ENTERED BY USER.
db 133 dup(?) ;THE STRING.
digits dw 10 dup(0) ;ARRAY OF 10 COUNTERS FOR DIGITS.
uppercase dw 26 dup(0) ;ARRAY OF 26 COUNTERS FOR UPPERCASE LETTERS.
lowercase dw 26 dup(0) ;ARRAY OF 26 COUNTERS FOR LOWERCASE LETTERS.
str db 6 dup('$') ;TO STORE NUMBER CONVERTED TO STRING.
char db ' =$'
space db ' $'
.code
;INITIALIZE DATA SEGMENT.
mov ax,@data
mov ds,ax
;DISPLAY MESSAGE TO CAPTURE LINE OF TEXT.
mov ah, 9
mov dx, offset msj1
int 21h
;CAPTURE LINE OF TEXT.
mov ah, 0AH
mov dx, offset text
int 21h
;COUNT CHARACTERS OF THE LINE OF TEXT.
mov si, offset text + 2 ;SI POINTS TO BEGINNING OF STRING. THE FIRST
;2 BYTES ARE FOR CONTROL (MAX LENGTH, LENGTH).
repeat:
;CHECK IF CURRENT CHARACTER IS DIGIT, UPPERCASE OR LOWERCASE.
mov bl, [ si ] ;GET CURRENT CHARACTER.
call check_digit
call check_uppercase
call check_lowercase
;NEXT CHARACTER.
inc si
mov bl, [ si ]
cmp bl, 13
jne repeat ;IF NEXT CHARACTER IS NOT 13, REPEAT.
;DISPLAY COUNTERS.
call clear_screen
call display_digit_counters
call display_uppercase_counters
call display_lowercase_counters
;WAIT FOR A KEY.
mov ah,7
int 21h
;FINISH PROGRAM.
mov ax,4c00h
int 21h
;------------------------------------------
;THIS PROCEDURE DISPLAYS THE 10 COUNTERS OF THE
;ARRAY OF COUNTERS FOR DIGITS.
proc display_digit_counters
mov di, offset digits ;DI POINTS TO ARRAY.
mov bp, 10 ;ARRAY LENGTH.
display_digits:
;DISPLAY SPACE.
mov ah, 9
mov dx, offset space
int 21h
;CONVERT CURRENT COUNTER IN STRING.
call dollars
mov ax, [ di ]
call number2string ;STRING RETURNS IN VARIABLE "STR".
;CONVERT CURRENT COUNTER IN CORRESPONDING DIGIT. FOR
;EXAMPLE, IF CURRENT COUNTER IS 3, CONVERT 3 IN '3'.
mov ax, di ;GET CURRENT COUNTER OFFSET.
sub ax, offset digits ;CONVERT TO 0..9.
shr ax, 1 ;DIVIDE OFFSET BECAUSE COUNTERS ARE DW.
add al, 48 ;CONVERT TO '0'..'9'.
mov char, al ;STORE DIGIT IN STRING TO DISPLAY.
;DISPLAY DIGIT FOR CURRENT COUNTER BELONGS TO.
mov ah, 9
mov dx, offset char
int 21h
;DISPLAY CURRENT COUNTER.
mov ah, 9
mov dx, offset str
int 21h
;NEXT COUNTER TO DISPLAY.
add di, 2 ;EVERY COUNTER IS DW (TWO BYTES).
dec bp
jnz display_digits ;IF ( BP > 0 ) JUMP.
ret
endp
;------------------------------------------
;THIS PROCEDURE DISPLAYS THE 26 COUNTERS OF THE
;ARRAY OF COUNTERS FOR UPPERCASE LETTERS.
proc display_uppercase_counters
mov di, offset uppercase ;DI POINTS TO ARRAY.
mov bp, 26 ;ARRAY LENGTH.
display_uppercase:
;DISPLAY SPACE.
mov ah, 9
mov dx, offset space
int 21h
;CONVERT CURRENT COUNTER IN STRING.
call dollars
mov ax, [ di ]
call number2string ;STRING RETURNS IN VARIABLE "STR".
;CONVERT CURRENT COUNTER IN CORRESPONDING LETTER. FOR
;EXAMPLE, IF CURRENT COUNTER IS 3, CONVERT 3 IN 'D'.
mov ax, di ;GET CURRENT COUNTER OFFSET.
sub ax, offset uppercase ;CONVERT TO 0..25.
shr ax, 1 ;DIVIDE OFFSET BECAUSE COUNTERS ARE DW.
add al, 65 ;CONVERT TO 'A'..'Z'.
mov char, al ;STORE LETTER IN STRING TO DISPLAY.
;DISPLAY LETTER FOR CURRENT COUNTER BELONGS TO.
mov ah, 9
mov dx, offset char
int 21h
;DISPLAY CURRENT COUNTER.
mov ah, 9
mov dx, offset str
int 21h
;NEXT COUNTER TO DISPLAY.
add di, 2 ;EVERY COUNTER IS DW (TWO BYTES).
dec bp
jnz display_uppercase ;IF ( BP > 0 ) JUMP.
ret
endp
;------------------------------------------
;THIS PROCEDURE DISPLAYS THE 26 COUNTERS OF THE
;ARRAY OF COUNTERS FOR LOWERCASE LETTERS.
proc display_lowercase_counters
mov di, offset lowercase ;DI POINTS TO ARRAY.
mov bp, 26 ;ARRAY LENGTH.
display_lowercase:
;DISPLAY SPACE.
mov ah, 9
mov dx, offset space
int 21h
;CONVERT CURRENT COUNTER IN STRING.
call dollars
mov ax, [ di ]
call number2string ;STRING RETURNS IN VARIABLE "STR".
;CONVERT CURRENT COUNTER IN CORRESPONDING LETTER. FOR
;EXAMPLE, IF CURRENT COUNTER IS 3, CONVERT 3 IN 'd'.
mov ax, di ;GET CURRENT COUNTER OFFSET.
sub ax, offset lowercase ;CONVERT TO 0..25.
shr ax, 1 ;DIVIDE OFFSET BECAUSE COUNTERS ARE DW.
add al, 97 ;CONVERT TO 'a'..'z'.
mov char, al ;STORE LETTER IN STRING TO DISPLAY.
;DISPLAY LETTER FOR CURRENT COUNTER BELONGS TO.
mov ah, 9
mov dx, offset char
int 21h
;DISPLAY CURRENT COUNTER.
mov ah, 9
mov dx, offset str
int 21h
;NEXT COUNTER TO DISPLAY.
add di, 2 ;EVERY COUNTER IS DW (TWO BYTES).
dec bp
jnz display_lowercase ;IF ( BP > 0 ) JUMP.
ret
endp
;------------------------------------------
;THIS PROCEDURE CHECK IF THE CHARACTER IN BL
;IS A DIGIT ('0'..'9'). IF IT IS, INCREASES
;THE APPROPIATE COUNTER IN ARRAY "DIGITS".
proc check_digit
cmp bl, '0'
jb not_a_digit ;IF AL < '0' FINISH.
cmp bl, '9'
ja not_a_digit ;IF AL > '9' FINISH.
;IF NO JUMP, AL IS A DIGIT. INCREASE COUNTER USING
;THE DIGIT AS OFFSET INSIDE THE ARRAY OF COUNTERS,
;FOR EXAMPLE, THE COUNTER FOR DIGIT '3' IS THE THIRD
;POSITION IN THE ARRAY.
sub bl, 48 ;CONVERT DIGIT IN NUMBER ('0'..'9'->0..9).
mov bh, 0 ;CLEAR BH TO USE BX.
shl bx, 1 ;MULTIPLY BY 2 BECASE EVERY COUNTER IS DW (2 BYTES).
add bx, offset digits ;BX POINTS TO THE APPROPIATE COUNTER.
inc [ bx ] ;INCREASE COUNTER OF DIGIT.
not_a_digit:
ret
endp
;------------------------------------------
;THIS PROCEDURE CHECK IF THE CHARACTER IN BL IS
;AN UPPERCASE LETTER ('A'..'Z'). IF IT IS, INCREASE
;THE APPROPIATE COUNTER IN ARRAY "UPPERCASE'.
proc check_uppercase
cmp bl, 'A'
jb not_uppercase ;IF AL < 'A' FINISH.
cmp bl, 'Z'
ja not_uppercase ;IF AL > 'Z' FINISH.
;IF NO JUMP, AL IS AN UPPERCASE LETTER. INCREASE COUNTER
;USING THE LETTER AS OFFSET INSIDE THE ARRAY OF COUNTERS,
;FOR EXAMPLE, THE COUNTER FOR LETTER 'C' IS THE THIRD
;POSITION IN THE ARRAY.
sub bl, 65 ;CONVERT DIGIT IN NUMBER ('A'..'Z'->0..25).
mov bh, 0 ;CLEAR BH TO USE BX.
shl bx, 1 ;MULTIPLY BY 2 BECASE EVERY COUNTER IS DW (2 BYTES).
add bx, offset uppercase ;BX POINTS TO THE APPROPIATE COUNTER.
inc [ bx ] ;INCREASE COUNTER OF UPPERCASE LETTER.
not_uppercase:
ret
endp
;------------------------------------------
;THIS PROCEDURE CHECK IF THE CHARACTER IN BL IS
;A LOWERCASE LETTER ('a'..'z'). IF IT IS, INCREASE
;THE APPROPIATE COUNTER IN ARRAY "LOWERCASE".
proc check_lowercase
cmp bl, 'a'
jb not_lowercase ;IF AL < 'a' FINISH.
cmp bl, 'z'
ja not_lowercase ;IF AL > 'z' FINISH.
;IF NO JUMP, AL IS A LOWERCASE LETTER. INCREASE COUNTER
;USING THE LETTER AS OFFSET INSIDE THE ARRAY OF COUNTERS,
;FOR EXAMPLE, THE COUNTER FOR LETTER 'c' IS THE THIRD
;POSITION IN THE ARRAY.
sub bl, 97 ;CONVERT LETTER IN NUMBER ('a'..'z'->0..25).
mov bh, 0 ;CLEAR BH TO USE BX.
shl bx, 1 ;MULTIPLY BY 2 BECASE EVERY COUNTER IS DW (2 BYTES).
add bx, offset lowercase ;BX POINTS TO THE APPROPIATE COUNTER.
inc [ bx ] ;INCREASE COUNTER OF LOWERCASE LETTER.
not_lowercase:
ret
endp
;------------------------------------------
;NUMBER TO CONVERT MUST ENTER IN AX.
;ALGORITHM : EXTRACT DIGITS ONE BY ONE, STORE
;THEM IN STACK, THEN EXTRACT THEM IN REVERSE
;ORDER TO CONSTRUCT STRING.
proc number2string
mov bx, 10 ;DIGITS ARE EXTRACTED DIVIDING BY 10.
mov cx, 0 ;COUNTER FOR EXTRACTED DIGITS.
cycle1:
mov dx, 0 ;NECESSARY TO DIVIDE BY BX.
div bx ;DX:AX / 10 = AX:QUOTIENT DX:REMAINDER.
push dx ;PRESERVE DIGIT EXTRACTED FOR LATER.
inc cx ;INCREASE COUNTER FOR EVERY DIGIT EXTRACTED.
cmp ax, 0 ;IF NUMBER IS
jne cycle1 ;NOT ZERO, LOOP.
;NOW RETRIEVE PUSHED DIGITS.
mov si, offset str
cycle2:
pop dx
add dl, 48 ;CONVERT DIGIT TO CHARACTER.
mov [ si ], dl
inc si
loop cycle2
ret
endp
;------------------------------------------
;FILLS VARIABLE STR WITH '$'.
;USED BEFORE CONVERT NUMBERS TO STRING, BECAUSE
;THESE STRINGS WILL BE DISPLAYED.
proc dollars
mov si, offset str
mov cx, 6
six_dollars:
mov al, '$'
mov [ si ], al
inc si
loop six_dollars
ret
endp
;------------------------------------------
proc clear_screen
mov ah,0
mov al,3
int 10H
ret
endp
In data segment you'll find three arrays : digits, lowercase and uppercase. These are the counters. The idea is to use the same characters as offset inside the array. For example, the first position of array digits is the counter for digit 0. The first position of array lowercase is the counter for 'a'.
The code is structured in procedures, some of them are very alike: check_digit (check_lowercase, check_uppercase), display_digit_counters (display_lowercase_counters, display_uppercase_counters).
Now the same program for Visual Studio 2010 C++ console project. Next code must be copy-pasted in the code file that contains the main method ("int _tmain(int argc, _TCHAR* argv[ ]") :
#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"
#include <string>
using namespace std;
char msj1[50];
char text[135];
int arr_digits[ 10 ];
int arr_lowercase[ 26 ];
int arr_uppercase[ 26 ];
char str[6];
char charr;
char char_equal;
char space[4];
int idx,i,tmp;
void printspace () {
printf( space );
}
void printstr () {
printf( str );
}
void print_char_equal () {
printf( "%c",char_equal );
}
void print_digit_counter () {
printf( "%d",arr_digits[ i ] );
}
// THIS PROCEDURE DISPLAYS THE 10 COUNTERS OF THE
// ARRAY OF COUNTERS FOR DIGITS.
void printcharr_digit () {
printf( "%c",i+48 );
}
void display_digit_counters () {
__asm {
mov i, 0
display_digits:
call printspace
call printcharr_digit
call print_char_equal
call print_digit_counter
;NEXT COUNTER TO DISPLAY.
inc i
cmp i, 10
jne display_digits ;IF ( I != 10 ) JUMP.
}
}
//;THIS PROCEDURE DISPLAYS THE 26 COUNTERS OF THE
//;ARRAY OF COUNTERS FOR UPPERCASE LETTERS.
void printcharr_uppercase () {
printf( "%c",i+65 );
}
void print_uppercase_counter () {
printf( "%d",arr_uppercase[ i ] );
}
void display_uppercase_counters () {
__asm {
mov i, 0
display_uppercase:
call printspace
call printcharr_uppercase
call print_char_equal
call print_uppercase_counter
;NEXT COUNTER TO DISPLAY.
inc i
cmp i, 26
jne display_uppercase ;IF ( I != 26 ) JUMP.
}
}
//;THIS PROCEDURE DISPLAYS THE 26 COUNTERS OF THE
//;ARRAY OF COUNTERS FOR LOWERCASE LETTERS.
void printcharr_lowercase () {
printf( "%c",i+97 );
}
void print_lowercase_counter () {
printf( "%d",arr_lowercase[ i ] );
}
void display_lowercase_counters () {
__asm {
mov i, 0
display_lowercase:
call printspace
call printcharr_lowercase
call print_char_equal
call print_lowercase_counter
;NEXT COUNTER TO DISPLAY.
inc i
cmp i, 26
jne display_lowercase ;IF ( I != 10 ) JUMP.
}
}
//;THIS PROCEDURE CHECK IF THE CHARACTER IN BL
//;IS A DIGIT ('0'..'9'). IF IT IS, INCREASES
//;THE APPROPIATE COUNTER IN ARRAY "DIGITS".
void inc_digit () {
arr_digits[ idx ]++;
}
void check_digit () {
__asm {
and ebx, 0ffh
cmp bl, '0'
jb not_a_digit ;IF AL < '0' FINISH.
cmp bl, '9'
ja not_a_digit ;IF AL > '9' FINISH.
;IF NO JUMP, AL IS A DIGIT. INCREASE COUNTER USING
;THE DIGIT AS OFFSET INSIDE THE ARRAY OF COUNTERS,
;FOR EXAMPLE, THE COUNTER FOR DIGIT '3' IS THE THIRD
;POSITION IN THE ARRAY.
sub bl, 48 ;CONVERT DIGIT IN NUMBER ('0'..'9'->0..9).
mov idx, ebx
call inc_digit
not_a_digit:
}
}
//;THIS PROCEDURE CHECK IF THE CHARACTER IN BL IS
//;AN UPPERCASE LETTER ('A'..'Z'). IF IT IS, INCREASE
//;THE APPROPIATE COUNTER IN ARRAY "UPPERCASE'.
void inc_uppercase () {
arr_uppercase[ idx ]++;
}
void check_uppercase () {
__asm {
and ebx, 0ffh
cmp bl, 'A'
jb not_uppercase ;IF AL < 'A' FINISH.
cmp bl, 'Z'
ja not_uppercase ;IF AL > 'Z' FINISH.
;IF NO JUMP, AL IS AN UPPERCASE LETTER. INCREASE COUNTER
;USING THE LETTER AS OFFSET INSIDE THE ARRAY OF COUNTERS,
;FOR EXAMPLE, THE COUNTER FOR LETTER 'C' IS THE THIRD
;POSITION IN THE ARRAY.
sub bl, 65 ;CONVERT DIGIT IN NUMBER ('A'..'Z'->0..25).
mov idx, ebx
call inc_uppercase
not_uppercase:
}
}
//;THIS PROCEDURE CHECK IF THE CHARACTER IN BL IS
//;A LOWERCASE LETTER ('a'..'z'). IF IT IS, INCREASE
//;THE APPROPIATE COUNTER IN ARRAY "LOWERCASE".
void inc_lowercase () {
arr_lowercase[ idx ]++;
}
void check_lowercase () {
__asm {
and ebx, 0ffh
cmp bl, 'a'
jb not_lowercase ;IF AL < 'a' FINISH.
cmp bl, 'z'
ja not_lowercase ;IF AL > 'z' FINISH.
;IF NO JUMP, AL IS A LOWERCASE LETTER. INCREASE COUNTER
;USING THE LETTER AS OFFSET INSIDE THE ARRAY OF COUNTERS,
;FOR EXAMPLE, THE COUNTER FOR LETTER 'c' IS THE THIRD
;POSITION IN THE ARRAY.
sub bl, 97 ;CONVERT LETTER IN NUMBER ('a'..'z'->0..25).
mov idx, ebx
call inc_lowercase
not_lowercase:
}
}
int _tmain(int argc, _TCHAR* argv[])
{
strcpy( msj1,"Enter a line of text (132 max) : " );
char_equal = '=';
strcpy( space," " );
printf( msj1 );
scanf( "%s",text );
__asm {
;COUNT CHARACTERS OF THE LINE OF TEXT.
lea esi, text ;SI POINTS TO BEGINNING OF STRING. THE FIRST
;2 BYTES ARE FOR CONTROL (MAX LENGTH, LENGTH).
repeat:
;CHECK IF CURRENT CHARACTER IS DIGIT, UPPERCASE OR LOWERCASE.
mov tmp, esi
mov bl, [ esi ] ;GET CURRENT CHARACTER.
call check_digit
mov esi, tmp
mov bl, [ esi ] ;GET CURRENT CHARACTER.
call check_uppercase
mov esi, tmp
mov bl, [ esi ] ;GET CURRENT CHARACTER.
call check_lowercase
mov esi, tmp
;NEXT CHARACTER.
inc esi
mov bl, [ esi ]
cmp bl, 0
jne repeat ;IF NEXT CHARACTER IS NOT 13, REPEAT.
;DISPLAY COUNTERS.
call display_digit_counters
call display_uppercase_counters
call display_lowercase_counters
}
scanf( "%s",text );
return 0;
}
Visual Studio 2010 doesn't allow to use interrupts (like int 21h or int 10h), so, printf and scanf are used instead. Important : notice the procedures respect the "top-down" style, where procedures must be declared before (above) they are called.