TASM 8086 Trying Count the characters of a string and display them in a table - x86-16

Please forgive some of the spacing in the program, the editor I am using displays tabs with a different spacing than how they are displayed here
Currently attempting to count the characters of a user-input string. I have thoroughly searched for help over the internet, but it seems that there is not that much current help when it comes to coding in 8086. Here is what I have so far:
.MODEL small
STACK 256
;************************************************************************
;* Equates Section *
;************************************************************************
EOS EQU 0 ;End of string
maxLen EQU 255 ;Maximum entry string length
bell EQU 7 ;Bell character -- <ctrl>G
;************************************************************************
;* Data Section *
;************************************************************************
.DATA
excode db 0 ;DOS error code
enter1 db 'Enter a string: ',EOS ;Prompt user for input
enter2 db 'Would you like to count another string?',EOS
;Prompt user for another string
buffer db maxLen dup (?) ;input/output buffer
buffer1 db maxLen dup (?) ;input/output buffer
spaces db ' ',EOS ;Used for spacing
letter db 'Letter ',EOS ;String used for first column
freq db 'Frequency',EOS ;String used for second column
cBord1 db '------ ',EOS ;String used to mark column border
cBord2 db '---------', EOS ;String used to makr column border
currLet dw 0
strLet dw 0
side db 0 ;0=Left side, 1=Right side
count db 0
exitProg db 'Exiting program',EOS ;Exit message
;************************************************************************
;* Code Section *
;************************************************************************
.CODE
;************************************************************************
;* External procedures from BINASC.OBJ *
;************************************************************************
EXTRN AscToBin:proc, binToAscDec:proc
;************************************************************************
;* External proceduers from STRING.OBJ & STRIO.OBJ *
;************************************************************************
EXTRN StrLength:proc, StrRead:proc
EXTRN StrWrite:proc, Newline:proc
;************************************************************************
;* Main entry point of program *
;************************************************************************
Start:
mov ax, #data ;Initialize DS to address
mov ds, ax ; of data segment
mov es, ax ;make es = ds
;************************************************************************
;* Get user input and conver to uppercase *
;************************************************************************
First:
call Newline ;Start new display line
lea di, enter1 ;Display message to enter a string
call StrWrite
mov si, 0
lea di, buffer ;Get user input
mov cx, maxLen ;Maximum string length
call StrRead ;Get entry from keyboard
lea si, buffer ;Move string into si
Back:
mov al, [si] ;Move si location to al
cmp al, 'a' ;Compare al to 'a'
jb Ahead ;Jump if less than
cmp al, 'z' ;Compare al to 'z'
ja Ahead ;Jump if greater than
and al, 0DFh ;Convert letter to uppercase
mov [si], al
Ahead:
inc si ;increment value of si
loop Back ;loop back to
;Display table head
call Newline
lea di, buffer
call StrWrite
call Newline
lea di, letter
call StrWrite
lea di, freq
call StrWrite
lea di, spaces
call StrWrite
lea di, letter
call StrWrite
lea di, freq
call StrWrite
call Newline
lea di, cBord1
call StrWrite
lea di, cBord2
call StrWrite
lea di, spaces
call StrWrite
lea di, cBord1
call StrWrite
lea di, cBord2
call StrWrite
call Newline
mov si, 0 ;clear si
ploop:
mov ax, si ;set ax register equal to si
add al, 'A' ;Move 'A' into low bit of ax
mov currLet, ax ;Move ax reigster into currLet
mov side, 0 ;set side to Left
sloop:
mov buffer1, al ;Move current letter into buffer1
mov buffer1+1, EOS ;clear end of buffer1
lea di, buffer1 ;Move letter into di
call StrWrite ;Write letter to screen
;Separate letters from their counts
lea di, spaces
call StrWrite
call StrWrite
mov count, 0 ;clear count
mov cx, 0 ;clear cx register
mov cx, maxLen ;set cx to maxLen
CntBack:
lea di, buffer ;Move input string into di
mov bl, [di] ;Move di location to bl
mov strLet, bx ;Move current letter of string
jmp CntFwrd
CntUp:
inc count
inc di
loop CntBack
CntFwrd:
mov ax, strLet
cmp ax, currLet ;compare string letter and current letter
je CntUp
inc di
loop CntBack
mov ax, 0
mov cx, 3
mov al, count
lea di, count
call binToAscDec
call StrWrite
cmp side, 1
je nextRow
inc side
;right side
lea di, spaces
call StrWrite
call StrWrite
call StrWrite
add currlet, 13
mov ax, currlet
jmp sloop
nextRow:
call Newline
inc si
cmp si, 13
je Exit
jmp ploop
;************************************************************************
;* Exit program *
;************************************************************************
Exit:
call Newline
call Newline
lea di, exitProg ;Call exit program message
call StrWrite
;************************************************************************
;* Program termination code *
;************************************************************************
mov ah, 04Ch ;DOS function Exit Program
mov al, excode ;Return exit code value
int 21h ;Call DOS. Terminate program
END Start ; End of program/entry point
I believe that my problem is in this snippet of the code:
CntBack:
lea di, buffer ;Move input string into di
mov bl, [di] ;Move di location to bl
mov strLet, bx ;Move current letter of string
jmp CntFwrd
CntUp:
inc count
inc di
loop CntBack
CntFwrd:
mov ax, strLet
cmp ax, currLet ;compare string letter and current letter
je CntUp
inc di
loop CntBack
If I were to enter the string 'hello' the program should display this:
Letter Frequency Letter Frequency
------ --------- ------ ---------
A 000 N 000
B 000 O 001
C 000 P 000
D 000 Q 000
E 001 R 000
F 000 S 000
G 000 T 000
H 001 U 000
I 000 V 000
J 000 W 000
K 000 X 000
L 002 Y 000
M 000 Z 000
But I will instead receive something like this in the display:
Letter Frequency Letter Frequency
------ --------- ------ ---------
A 000 N 000
B 000 O 000
C 000 P 000
D 000 Q 000
E 000 R 000
F 000 S 000
G 000 T 000
H _
It seems that the code is not counting the entire input string, and instead is only comparing the first strLet, which is the first letter of the string, with the currlet value

At the top of the CntBack loop, you're setting DI to point to the start of the input buffer. After you compare the first character and increment DI, you loop back to CntBack which resets DI to point to the same first character of the input buffer.
So, you're just comparing against the first character of your string over and over until your CX count is exhausted. Try moving the CntBack label down one instruction, and see if that fixes the problem.

Related

Counting letters and printing an array of numbers with windows api

I have an user input string, which was lowercased and all special characters removed target2 and I want to count how many times each letter appears on the string, and then print the array with 26 letters. However, it prints just a blank line. Is the error when I add to the array? when I print from the array, or both?
If I watch letterArray, it says 1 '\x1' What does that mean?
lettersArray byte 26 dup(0)
countingLetters proc
; clear esi and edi
mov esi, 0
mov edi, 0
charloop
mov al, target2[esi] ; Get a character from the string
cmp al, 97 ; Check if its not a letter
jb printloop ; If bellow, print
sub eax, 97 ; so that 'a' = 0, 'z' = 26.
mov dl, lettersArray[eax]
inc dl
mov lettersArray[eax], dl
inc esi
jmp charloop ; repeat
printloop:
mov bl, lettersArray[edx * type lettersArray]
add bl, 48
push edx
invoke WriteConsoleA, consoleOutHandle, ebx, 4, bytesWritten, 0
pop edx
inc edx
cmp edx, 25 ;Are we done?
ja done ;if yes
jmp printloop ; Repeat
done:
ret
countingLetters endp
This should count the appearances of each letter and print an array with 26 elements, i.e: 00102000000000[etc]

How to print an integer stored in a variable

So I have a program in 8086 assembly that allows the user to enter 2 digits, store them in a variable and then print out the number:
data segment
broj db ?
ends
stack segment
dw 128 dup(0)
ends
code segment
mov ax, data
mov ds, ax
mov es, ax
mov ah, 1h
int 21h
sub al, 48d
mov bl, 10d
mul bl
mov broj, al
mov ah, 1h
int 21h
sub al, 48d
add broj, al
mov dl, broj
sub dl, 48d
mov ah, 2h
int 21h
mov ax, 4c00h
int 21h
ends
However whenever I enter a number for example 21 it doesn't give me the number instead it gives me ASCII Code for that value.
Can anyone help?!
However whenever I enter a number for example 21 it doesn't give me the number instead it gives me ASCII Code for that value.
If you feed (input) your program a number that consists of 2 digits, then you'll have to print also 2 digits! Currently your code contains just the one character output function.
First divide the number in broj by 10 giving a quotient (in AL) and a remainder (in AH).
Convert the quotient to character (Add 48) and print it.
Convert the remainder to character (Add 48) and print it.
Example:
mov al, broj
mov ah, 0
mov bl, 10
div bl
add ax, "00"
mov dx, ax
mov ah, 02h
int 21h
mov dl, dh
mov ah, 02h
int 21h

Algorithm to assembly x86?

I have been for some days trying to translate an algorithm to assembly x86, and I did it. However I would like to print the final result that it is saved in "tmp", what instruction can I use? (I'm Spanish so I'm sorry if I say something wrong in English).
This is my algorithm:
tmp = NOT(L0)
tmp = tmp AND L1
tmp = NOT(NOT(tmp) OR NOT(L2))
tmp = NOT(tmp OR NOT(L3))
tmp = NOT(tmp + NOT(L4))
if (tmp == L5)
licence = correct
else
licence = incorrect
And this is it in assembly:
LicenceCorrect PROC
push ebp
mov ebp,esp
push ebx
push ecx
push edx
mov ebx, [ebp+8]
mov edx,[ebx]
mov ecx,edx
not ecx
mov edx,[ebx+4]
and ecx,edx
mov edx,[ebx+8]
not edx
not ecx
or ecx,edx
not ecx
mov edx,[ebx+16]
not edx
or ecx,edx
not ecx
;if
mov edx,[ebx]
cmp ecx,edx
jne cons
mov al,0
jmp next
cons:
mov al,1
next:
pop edx
pop ecx
pop ebx
pop ebp
ret
LicenceCorrect ENDP
END
Next code displays a number in AX (made with EMU8086). What MissPaper must do now is insert your procedure (LicenseCorrect) at the end of next code, and call it after "call dollars", then assign the value to AX (remove "12345").
Here it is for 32 bits:
.model small
.stack 100h
.data
buffer db 6 dup(?)
.code
start:
;INITIALIZE DATA SEGMENT.
mov ax, #data
mov ds, ax
;FIRST, FILL BUFFER WITH '$' (NECESSARY TO DISPLAY).
mov si, offset buffer
call dollars
;SECOND, CONVERT NUMBER TO STRING.
mov ax, 12345
mov si, offset buffer
call number2string
;THIRD, DISPLAY STRING.
mov dx, offset buffer
call printf
;FINISH PROGRAM.
mov ax, 4c00h
int 21h
;-----------------------------------------
;PARAMETER : DX POINTING TO '$' FINISHED STRING.
printf proc
mov ah, 9
int 21h
ret
printf endp
;------------------------------------------
;FILLS VARIABLE WITH '$'.
;USED BEFORE CONVERT NUMBERS TO STRING, BECAUSE
;THE STRING WILL BE DISPLAYED.
;PARAMETER : SI = POINTING TO STRING TO FILL.
dollars proc
mov cx, 6
six_dollars:
mov bl, '$'
mov [ si ], bl
inc si
loop six_dollars
ret
dollars endp
;------------------------------------------
;CONVERT A NUMBER IN STRING.
;ALGORITHM : EXTRACT DIGITS ONE BY ONE, STORE
;THEM IN STACK, THEN EXTRACT THEM IN REVERSE
;ORDER TO CONSTRUCT STRING (STR).
;PARAMETERS : AX = NUMBER TO CONVERT.
; SI = POINTING WHERE TO STORE STRING.
number2string proc
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.
cycle2:
pop dx
add dl, 48 ;CONVERT DIGIT TO CHARACTER.
mov [ si ], dl
inc si
loop cycle2
ret
number2string endp
end start
Now the 64 bits version (for much bigger numbers in EAX), made with GUI Turbo Assembler x64 (http://sourceforge.net/projects/guitasm8086/):
.model small
.586
.stack 100h
.data
buffer db 11 dup(?)
.code
start:
;INITIALIZE DATA SEGMENT.
mov ax, #data
mov ds, ax
;FIRST, FILL BUFFER WITH '$' (NECESSARY TO DISPLAY).
mov esi, offset buffer
call dollars
;SECOND, CONVERT NUMBER TO STRING.
mov eax, 1234567890
mov esi, offset buffer
call number2string
;THIRD, DISPLAY STRING.
mov dx, offset buffer
call printf
;FINISH PROGRAM.
mov ax, 4c00h
int 21h
;-----------------------------------------
;PARAMETER : DX POINTING TO '$' FINISHED STRING.
printf proc
mov ah, 9
int 21h
ret
printf endp
;------------------------------------------
;FILLS VARIABLE WITH '$'.
;USED BEFORE CONVERT NUMBERS TO STRING, BECAUSE
;THE STRING WILL BE DISPLAYED.
;PARAMETER : ESI = POINTING TO STRING TO FILL.
dollars proc
mov cx, 11
six_dollars:
mov bl, '$'
mov [ esi ], bl
inc esi
loop six_dollars
ret
dollars endp
;------------------------------------------
;CONVERT A NUMBER IN STRING.
;ALGORITHM : EXTRACT DIGITS ONE BY ONE, STORE
;THEM IN STACK, THEN EXTRACT THEM IN REVERSE
;ORDER TO CONSTRUCT STRING (STR).
;PARAMETERS : EAX = NUMBER TO CONVERT.
; ESI = POINTING WHERE TO STORE STRING.
number2string proc
mov ebx, 10 ;DIGITS ARE EXTRACTED DIVIDING BY 10.
mov cx, 0 ;COUNTER FOR EXTRACTED DIGITS.
cycle1:
mov edx, 0 ;NECESSARY TO DIVIDE BY EBX.
div ebx ;EDX:EAX / 10 = EAX:QUOTIENT EDX:REMAINDER.
push dx ;PRESERVE DIGIT EXTRACTED (DL) FOR LATER.
inc cx ;INCREASE COUNTER FOR EVERY DIGIT EXTRACTED.
cmp eax, 0 ;IF NUMBER IS
jne cycle1 ;NOT ZERO, LOOP.
;NOW RETRIEVE PUSHED DIGITS.
cycle2:
pop dx
add dl, 48 ;CONVERT DIGIT TO CHARACTER.
mov [ esi ], dl
inc esi
loop cycle2
ret
number2string endp
end start
I didn't add your procedure because it uses the stack and I don't know what values to push before calling it.

Assembler (TASM x64) arrays and elements

I have an array of nine names:
.model tiny
.data
vardas1 db "Rokas",0ah,'$'
vardas2 db "Tomas",0ah,'$'
vardas3 db "Matas",0ah,'$'
vardas4 db "Domas",0ah,'$'
vardas5 db "Augis",0ah,'$'
vardas6 db "Vofka",0ah,'$'
vardas7 db "Marka",0ah,'$'
vardas8 db "Auris",0ah,'$'
vardas9 db "Edvis",0ah,'$'
vardai dw offset vardas1, offset vardas2, offset vardas3, offset vardas4, offset vardas5, offset vardas6, offset vardas7, offset vardas8, offset vardas9
.code
org 100h
I need to read a digit from keyboard, and then I need to print that name. For example I will push 5, and console should write "Augis". BTW, second code block aren't all code, just loop that doesn't work
paieska:
mov dx, offset _comment1 ; Just string name asking user to input digit
mov ah, 9
int 21h
mov j, 00h ; Trying to input the digit from keyboard
mov ah, 01h
mov dl, 0ah
int 21h
mov bx, offset vardai ; Add array "names" to bx register
add bx, cx ; Add cx for indexing
mov dx, [bx] ; Add first array element to dx register
add cx, 2 ; Increasing cx by 2, because I'm using data word not data byte
mov ah, 9 ; Try to print it
int 21h
cmp cx, j ; Try to compare cx (index of array) to mine inputed digit "j"
jne paieska
je end
mov ah, 01h
mov dl, 0ah ;NO NEED FOR THIS - INT21/01 DOES NOT USE DL
int 21h
MOV AH, '1' ; MIN INPUT CHAR
mov bx, offset vardai ; Add array "names" to bx register WELL, ASSIGN ACTUALLY
MOV CX,2 ;NUMBER OF BYTES TO ADD (WORDS, NOT BYTES)
LOOPN:
mov dx, [bx] ; name-pointer array element to dx register
CMP AH,AL ; MATCHING char?
JE PNAME ; YES, PRINT NAME
add bx, cx ; Add cx=2 for next name
inc AH ; next possible character input
CMP AH,'9'+1 ; allowed is '1'..'9'
jne loopn ; in allowed range
; input not 1..9
mov dx, offset errormessage
PNAME:
mov ah, 9 ; Try to print it
int 21h
jmp end
Well, I tried to edit your approach with CAPS, but it became too complicated.
Essentially, you are reading a character from the keyboard using function 01. This character arrives in AL. If all goes well, it should be '1'..'9'. Notice these are the ASCII characters '1'..'9', that is hex 31..39
Next step is to set BX to the start of the table, AH to the minimum character you anticipate and CX to 2 because the table contains words, not bytes.
Now we have a loop. Load X from the table, and check whether AL is equal to AH. If the user input 1, these will be equal, so go print the string.
Otherwise, add 2 to BX to point to the next entry in the table (this could have been done by ADD BX,2 or INC BX INC BX which would mean the MOV CX,2 would be unnecessary - just the way I wrote it...) and increment the '1' in AH to '2'.
The end-condition for the loop is when AH gets incremented from '9' to - well, ':' or '9'+1. If it hasn't reached that end-condition, then run around the loop until all of the values '1'..'9' have been tested. If you haven't got to PNAME yet, then there's an error because the character input wasn't allowed, so point to an error message and then print it.
Now jumping to the end - probably you'd want to terminate the program, so you'd execute
MOV AH,4CH
INT 21H

First macro assembler program, can't figure our unhandled exception

This is my first assembler program in masm32. Using vis studio 2012. And this is just one procedure in a program to convert input into an ascii chart with decimal, hex and ascii output. I've been trying to figure this out for 8 hours, and I know it's going to be something really simple.
It gets through all the computation, but during the pop and return phase it crashes into an unhandled exception when accessing the EIP(i think). Also, all my registers are set to 0 except ebx and I don't know why, but it may have something to do with it.
This is just the procedure to convert from the input string to a decimal value.*
My inputStr is:
inputStr db 16 DUP(0)
.code
main proc
xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx
lea esi, outputStr1
call PrintString
lea esi, inputStr
call GetString
call StrtoNum
invoke ExitProcess, 0 ;****This is the next line when it crashes***
main endp
StrtoNum proc ;going to hex first
pushad
pushfd
mov bl, 1 ;mov 1 into bl for later multiplying
whilemorechar:
mov al,byte ptr [esi]
cmp al, 0 ;stuff
je ConvertDone ;if null then we are done here
;esi is pointing to the string to be converted
;cmp bl,0
;jnz decrement
cmp al, 0h
je ConvertDec
sub al, 30h ;get first string byte to dec number 0-9
push ax ;push last digit to stack
inc esi ;gets to next string byte
inc cl ;make note of decimal position in string
jmp whilemorechar ;jump for next place in string
ConvertDec: ;reverse is done, now turn into decimal number
cmp cl, 0 ;compare counter to 0
jz ConvertDone ;if counter is 0, start comparing numbers
pop ax ;pop last on stack
mul bl ;multiply place value by input byte
add dx, ax ;add decimal value into dl
mov al, 10d ;move 10 into al
mul bx ;multiply 10 and bl value to get next
mov bx, ax ;mov decimal place into bl for next loop
dec cl ;decrement counter
jmp ConvertDec ;loop through again for next decimal place
ConvertDone:
mov ebx, 0
popfd ;pop flags
popad ;pop registers
ret ;return to caller
StrtoNum endp

Resources