Assembly translating ASCII character to HEX value - nasm

I was modifying code that suppose to translate ASCII characters to hexadecimal values. My first version was working perfectly without any problems, however my newer function have some problems.
This is my new function which translates ASCII values to hexadecimal values:
;carry flag cleared if successed, if not then it is set
;input number should be in AL
;output number should be in AH
.translate:
xor AH,AH
mov BX, HEXASCII
.loop:
mov DL,[BX]
cmp DL,AL
je .end
inc BX
inc AH
cmp AH,0x10
je .err
jmp .loop
.end:
clc
ret
.err:
stc
ret
; ... some code
HEXASCII db '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
however this function seems to not work properly - when sending output from this function to int 13, bochs is returning error - read/write/verify parameter out of range. the numbers I use was 00 for Head, 00 for cylinder, and 01 for sector, so I guess that it is not an actual problem with arguments which I wrote.
I also used my previous function with the same arguments and it properly loaded sector I wanted to.
I tried to use Bochs debugger to track registers in memory before int 0x13, however it was looking like the registers have the same values I inputted.
My previous function is:
;carry flag cleared if successed, if not then it is set
;input number should be in AL
;output number should be in AH
.translate:
cmp AL,'0'
je .x0
cmp AL,'1'
je .x1
cmp AL,'2'
je .x2
cmp AL,'3'
je .x3
cmp AL,'4'
je .x4
cmp AL,'5'
je .x5
cmp AL,'6'
je .x6
cmp AL,'7'
je .x7
cmp AL,'8'
je .x8
cmp AL,'9'
je .x9
cmp AL,'a'
je .xA
cmp AL,'b'
je .xB
cmp AL,'c'
je .xC
cmp AL,'d'
je .xD
cmp AL,'e'
je .xE
cmp AL,'f'
je .xF
cmp AL,'A'
je .xA
cmp AL,'B'
je .xB
cmp AL,'C'
je .xC
cmp AL,'D'
je .xD
cmp AL,'E'
je .xE
cmp AL,'F'
je .xF
jmp .NONE
.x0:
xor AH,AH
clc
ret
.x1:
mov AH,0x1
clc
ret
.x2:
mov AH,0x2
clc
ret
.x3:
mov AH,0x3
clc
ret
.x4:
mov AH,0x4
clc
ret
.x5:
mov AH,0x5
clc
ret
.x6:
mov AH,0x6
clc
ret
.x7:
mov AH,0x7
clc
ret
.x8:
mov AH,0x8
clc
ret
.x9:
mov AH,0x9
clc
ret
.xA:
mov AH,0xA
clc
ret
.xB:
mov AH,0xB
clc
ret
.xC:
mov AH,0xC
clc
ret
.xD:
mov AH,0xD
clc
ret
.xE:
mov AH,0xE
clc
ret
.xF:
mov AH,0xF
clc
ret
.NONE:
xor AH,AH
stc
ret
I don't expect any other part of code to be damaged, as I did not modified it.
If it is needed I will include the full code, however it is pretty long.
Is there anything which is missing or wrong in the new function?
edit: Forgot to mention that the code is running in 16 bit real mode

After getting some help from osdev forum someone noticed that the function does translate the values properly, however it is damaging dl register which I forget to keep the same as it holds hard drive number.
If someone needs newer version of this code here you go:
.translate:
xor AH,AH
mov BX, HEXASCII
.loop:
cmp [BX],AL
je .end
inc BX
inc AH
cmp AH,0x10
je .err
jmp .loop
.end:
clc
ret
.err:
stc
ret
;... some code
HEXASCII db '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'

Related

How to check if there is newLine YASM 8086

I've been working on the project. The main goal is to calculate how many words do not contain letters from 'a' to 'k'. Given file has [0;1000] lines. Every line contains 6 columns.
The first two columns contain string with [1; 20] characters. Characters could be letters, numbers, and whitespaces.
3-5 columns contain integers in the range [-100; 100]. 6th column contains real numbers in the range [-9.99; 9.99] with only two digits after the decimal point.
Each section I separated by a semicolon ';'.
helloA;lB;lC;lD;lE;lF
A11;bas morning;0;0;5;1.15
My problem is, that if there is a newLine at the end of the file, skip them, and sum up them in the final count.
TASK: calculate how many words (the word is one or more symbols without ' '(space)) in the first two columns do not contain letters 'B' or 'C'. And print that integer number.
I have tried to compare al with 0x20 and jump to the next section if it is smaller, but that didn't help me.
What I have done so far
; Main
.whileNotTheEndOfFile:
mov si, 2
call procSkaitytiEilute
; Check the first two columns
;mov al, ';'
mov di, line
.skipSpaces:
mov al, [di]
inc di
cmp al, ' '
je .skipSpaces
cmp al, ';'
je .q3
dec di
.checkWord:
mov bx, 1
.q1:
mov al, [di]
inc di
cmp al, ' '
je .q2
cmp al, ';'
je .q2
jmp .q8
.q8:
cmp al, 20h
jl .skipLine
jmp .q7
.q7:
cmp al, 'A'
jl .q5
jmp .q4
.q4:
cmp al, 'K'
jae .q5
mov bx, 0 ; One or more invalid chars in current word
jmp .q1
.q5:
cmp al, 'a'
jae .q6
jmp .q1
.q6:
cmp al, 'k'
jae .q1
mov bx, 0
jmp .q1
.q2:
add [lineCount], bx ; BX=[0,1] Counting the 'good' words
cmp al, ';'
jne .skipSpaces
.q3:
dec si ; Next column?
jnz .skipSpaces
.skipLine:
cmp [readLastLine], byte 0
je .whileNotTheEndOfFile
; Jeigu ne failo pabaiga, kartojame cikla
.skipLine:
sub [lineCount], 2
cmp [readLastLine], byte 0
je .whileNotTheEndOfFile
; Hexadecimal convertion to decimal
mov dx, lineCount
mov ax, [lineCount]
call procUInt16ToStr
call procPutStr
macNewLine
mov si, dx
.writeToFile:
lodsb
cmp al, 0
jne .writeToFile
sub si, dx
lea cx, [si-1]
mov bx, [writingDescriptor]
mov ah, 40h
int 21h
; Closing Files
.end:
mov bx, [writingDescriptor]
call procFClose
.rasymoKlaida:
mov bx, [readingDescriptor]
call procFClose
exit
%include 'yasmlib.asm'
; void procSkaitytiEilute()
; Read line to buffer ‘eilute’
procReadLine:
push ax
push bx
push cx
push si
mov bx, [readingDescriptor]
mov si, 0
.loop:
call procFGetChar
; End if the end of file or error
cmp ax, 0
je .endOfFile
jc .endOfFile
; Putting symbol to buffer
mov [line+si], cl
inc si
; Check if there is \n?
cmp cl, 0x0A
je .endOfLine
jmp .loop
.endOfFile:
mov [readLastLine], byte 1
.endOfLine:
mov [line+si], byte '$'
mov [lineLength], si
pop si
pop cx
pop bx
pop ax
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
section .data
readingFile:
db 'input.dat', 00
readingDescriptor:
dw 0000
writingFile:
times 128 db 00
writingDescriptor:
dw 0000
readLastLine:
db 00
line:
db 64
times 66 db '$'
lineLength:
dw 0000
lineCount:
times 128 db 00
My file:
XYZ;PPP;1;1;1;1.00
A11;bas aaa;0;0;5;1.15
My output: 4
Needed output: 2 (because there are only 2 "good" words "XYZ" and "PPP"), my program counts 2 more words because of a new line at the end.
It would appear that in contrast with the original task description your file can contain one or more empty lines as well. Or even lines with spaces followed by the newline codes.
To deal with these empty lines (only containing the bytes 13 and 10), your idea to skip on codes below 32 is good, but it's in the wrong place.
Below is the quick fix. Please notice that the ASCII codes are to be treated as unsigned numbers. Don't use the signed jl instruction on them.
mov di, line
.skipSpaces:
mov al, [di]
inc di
cmp al, ' '
je .skipSpaces
cmp al, ';'
je .q3
dec di
cmp al, 32 ; NEW
jb .skipLine ; NEW
.checkWord:
mov bx, 1
.q1:
mov al, [di]
inc di
cmp al, ' '
je .q2
cmp al, ';'
je .q2
! cmp al, 'A'
! jb .q1
! cmp al, 'K'
! jbe .badChar ; [A,K] is bad
! cmp al, 'a'
! jb .q1
! cmp al, 'k'
! ja .q1
! .badChar: ; [a,k] is bad
! mov bx, 0 ; One or more invalid chars in current word
jmp .q1
.q2:
add [lineCount], bx ; BX=[0,1] Counting the 'good' words
cmp al, ';'
jne .skipSpaces
.q3:
dec si ; Next column?
jnz .skipSpaces
.skipLine:
cmp [readLastLine], byte 0
je .whileNotTheEndOfFile
The lines that I marked with an exclamation mark really should be:
or al, 32 ; Make lowercase
cmp al, 'a'
jb .q1
cmp al, 'k'
ja .q1
mov bx, 0 ; [A,K] and [a,k] are badChars
Why does your program contain next redundant lines?
; Jeigu ne failo pabaiga, kartojame cikla
.skipLine:
sub [lineCount], 2
cmp [readLastLine], byte 0
je .whileNotTheEndOfFile

Storing a character in a memory location in Assembly Language

I am trying to write a program in Assembly Language where it will replace all the letter 'T' in the string STRVAR with 'U' and place the new string in OUTPUT. I figured that I should store each character one by one in OUTPUT throughout the loop process although after doing several trial and error with mov, I already ran out of ideas on how to store a character in a new memory location.
STRVAR db "ACGTACGTCCCTTT",0
OUTPUT times 21 db 0
section .text
global CMAIN
CMAIN:
;write your code here
lea esi, [STRVAR]
L1:
mov al, [esi]
cmp al, 0
JE FINISH
cmp al, 'T'
JE REPLACE
JNE CONTINUE
inc esi
jmp L1
REPLACE:
;store character here
inc esi
jmp L1
CONTINUE:
;store character here
inc esi
jmp L1
FINISH:
xor eax, eax
ret
I followed the information shared by Jester and I finally get to have the program work based on what is stated in the specification. I realized I need to add section .data and introduced another point which in this case, lea edi, [OUTPUT] to store each character and use it to print a new string.
%include "io.inc"
section .data
STRVAR db "ACGTACGTCCCTTT",0
OUTPUT times 21 db 0
section .text
global CMAIN
CMAIN:
;write your code here
lea esi, [STRVAR]
lea edi, [OUTPUT]
L1:
mov al, [esi]
cmp al, 0
JE FINISH
cmp al, 'T'
JE REPLACE
JNE CONTINUE
inc esi
inc edi
jmp L1
REPLACE:
mov byte[edi], 'U'
inc esi
inc edi
jmp L1
CONTINUE:
mov byte[edi], al
inc esi
inc edi
jmp L1
FINISH:
mov byte [edi], 0
PRINT_STRING OUTPUT
PRINT_DEC 1, [edi] ;check if the terminating 0 is also included
xor eax, eax
ret

Assembly x86 reversing sentence program

I am trying to make a program where the user have to enter an input, for example: Hello World and get an output: 'DLROw OLLEh'. Here is my program
org 100h
include emu8086.inc
.DATA
STR1 DB 0DH, 0AH, 'Input: $'
STR2 DB 0DH, 0AH, 'Output: $'
Nl DB 0Dh, 0Ah,'$'
.CODE
START:
MOV AX, #DATA
MOV DS, AX
DISP:
LEA DX,STR1
MOV AH,09H
INT 21H
MOV CL,00
MOV AH,01H
READ:
INT 21H
MOV BL, AL
PUSH BX
INC CX
CMP AL, 0DH
JZ DISPLAY
CMP AL, 'A' ; < then A
JB NotALetter
CMP AL, 'Z' ; > then Z
JA AGAIN ; repeat again
JMP CONTINUE1
AGAIN:
CMP AL, 'a' ; < then a
JB NotALetter
CMP AL, 'z' ; > then z
JA NotALetter
CONTINUE1:
JMP READ
DISPLAY:
LEA DX, STR2
MOV AH, 09h
INT 21H
LEA DX, NL
MOV AH, 09h
INT 21h
POP BX ; pop enter key
ANS:
MOV AH, 02h
POP BX ; pop the character
CMP BL, 'a' ; check if its in upper case
JB toLower ; if yes then jmp to toLower
SUB BL, 32 ; if not in upper case then convert to upper case
JMP CONTINUE2
toLower:
ADD BL, 32 ; convert to lower case
CMP BL, 20h
;SUB BL, 32
CONTINUE2:
MOV DL, BL
INT 21H
LOOP ANS
JMP EXIT ; if everything is fine jmp to exit
NotALetter:
printn
print "The input character is not a letter."
EXIT:
hlt
.EXIT
END START
I can enter any input but as soon as I enter any symbol, I am getting a message that this is a symbol, then program ends whereas I want to get the same output but still allow to enter a space character. I am really new in Assembly and moreover while I was trying to figure everything out I got even more lost.
If I comment out JB NotALetter and JA NotALetter, my space character becomes # probably because I am adding 20 to the ASCII hex number. Can someone please help to figure out this problem?
I can enter any input but as soon as I enter any symbol, I am getting
a message that this is a symbol, then program ends whereas I want to
get the same output but still allow to enter a space character.
As OP wants to capture the space without messing with symbol message. This can be achieved with the following:
In the READ label after you compare for enter key add this:
CMP AL, ' ' ; compare for space
JZ CONTINUE1
And in the ANS label after you pop bx add this:
CMP BL, ' ' ; if equal to space
JZ CONTINUE2 ; then print it by going to CONTINUE2 label
Just add an extra comparison to your toLower method as follows:
toLower:
CMP BL, 'A'
JL CONTINUE2
ADD BL, 32 ; convert to lower case
Complete code:
org 100h
include emu8086.inc
.DATA
STR1 DB 0DH, 0AH, 'Input: $'
STR2 DB 0DH, 0AH, 'Output: $'
Nl DB 0Dh, 0Ah,'$'
.CODE
START:
MOV AX, #DATA
MOV DS, AX
DISP:
LEA DX,STR1
MOV AH,09H
INT 21H
MOV CL,00
MOV AH,01H
READ:
INT 21H
MOV BL, AL
PUSH BX
INC CX
CMP AL, 0DH
JZ DISPLAY
CMP AL, 'A' ; < then A
JB CONTINUE1
CMP AL, 'Z' ; > then Z
JA AGAIN ; repeat again
JMP CONTINUE1
AGAIN:
CMP AL, 'a' ; < then a
JB CONTINUE1
CMP AL, 'z' ; > then z
JA CONTINUE1
CONTINUE1:
JMP READ
DISPLAY:
LEA DX, STR2
MOV AH, 09h
INT 21H
LEA DX, NL
MOV AH, 09h
INT 21h
POP BX ; pop enter key
ANS:
MOV AH, 02h
POP BX ; pop the character
CMP BL, 'a' ; check if its in upper case
JB toLower ; if yes then jmp to toLower
SUB BL, 32 ; if not in upper case then convert to upper case
JMP CONTINUE2
toLower:
CMP BL, 'A'
JL CONTINUE2
ADD BL, 32 ; convert to lower case
CONTINUE2:
MOV DL, BL
INT 21H
LOOP ANS
JMP EXIT ; if everything is fine jmp to exit
;NotALetter:
; printn
; print "The input character is not a letter."
EXIT:
hlt
.EXIT
END START
Input Hello World,
Output DLROw OLLEh
Also, you don't really need NotALetter method as you can notice, I just commented out.

Lowercase to Uppercase program not capitalizing when it should

I am writing an assembly program that capitalizes the first letter of each word in the sentence I input.
My problem is that it doesn't capitalize the first letter of the words. What's wrong with my code?
Below is my code
.model small
.stack 100h
.data
prompt1 db "Input String: $"
prompt2 db "Output String: $"
InputString db 21,?,21 dup("$")
newline db 10,13,"$"
.code
start:
mov ax, #data
mov ds, ax
; Getting input string
mov ah,09h
lea dx, prompt1
int 21h
lea si, InputString
mov ah, 0Ah
mov dx, si
int 21h
nextline:
mov ah, 09h
lea dx, newline
int 21h
loop1:
mov cl, [si+1]
mov ch,0
add si, cx
inc si
dec cx
cmp cx, 0
je exit
jmp checkspace
checkspace:
cmp si, " "
inc si
je checkletter
checkletter:
cmp si, "a"
jae checkletter2
checkletter2:
cmp si, "z"
jbe capital
capital:
mov ah, [si]
xor ah, 00100000b
mov [si], ah
jmp loop1
exit:
mov ah,09h
lea dx, prompt2
int 21h
mov ah, 09h
mov dx, offset InputString+2
int 21h
mov ah, 4ch
int 21h
end start
Below is the updated part of my code. It can now only capitalize the first letter of the word. I don't know if the problem is with the loop. Please help me figure out which part of my code is wrong. I'm so sorry I don't know how to use a debugger for checking. Thank you.
mov cl, [si+1]
mov ch,0
add si,2
jmp checkletter
loop1:
inc si
dec cx
cmp cx, 0
je exit
cmp si, " "
je checkletter3
jmp loop1
checkletter:
cmp si, 41h
jae checkletter2
checkletter2:
cmp si, 5Ah
jbe capital
checkletter3:
inc si
dec cx
jmp checkletter
capital:
mov ah, [si]
xor ah, 00100000b
mov [si], ah
jmp loop1
cmp si, " "
cmp si, "a"
cmp si, "z"
cmp si, 41h
cmp si, 5Ah
All of the above are comparing the address in SI with an immediate value. They are not comparing the byte that can be found at the address that SI is pointing to with the immediate! You need to dereference it.
cmp byte [si], " "
cmp byte [si], "a"
cmp byte [si], "z"
cmp byte [si], 41h
cmp byte [si], 5Ah
To solve the task of capitalizing just the first letter of each word
You first need to find the start of each word. This is done by skipping the whitespace in front of each word.
Once located, you compare to see if the starting character is in small caps, and only if it is, you convert it to uppercase by subtracting 32.
The rest of the word is again skipped until either the end of the line is encountered or another whitespace is found for which the process starts all over again from the top.
It look like this:
mov cl, [si+1]
test cl, cl ;Exit if the input was empty!!!
jz Exit
add si, 2 ;Point at first byte
SkipSpace:
mov al, [si]
cmp al, " "
jne FirstCharacter
inc si ;(*)
dec cl
jz Exit
jmp SkipSpace
FirstCharacter:
cmp al, "a"
jb SkipRemainingCharacters
cmp al, "z"
ja SkipRemainingCharacters
sub al, 32 ;Capitalize
mov [si], al ; and write back in string
SkipRemainingCharacters:
inc si
dec cl
jz Exit
mov al, [si]
cmp al, " " ;If not space then it's part of same word
jne SkipRemainingCharacters
jmp SkipSpace ;This could just as easily jump to (*)
Exit:

print string result in assembly

; multi-segment executable file template.
data segment
string db "THis is LuxUR in Summer."
ends
stack segment
dw 128 dup(0)
ends
code segment
start:
; set segment registers:
mov ax, data
mov ds, ax
mov es, ax
mov bx, offset string
mov al, 0 ; lower letters in word
mov dl,0 ; maximum letters
check:
mov cl, 41h ; from A-Z
mov ch, 5Ah
mov ah, [bx]
cmp ah, "."
je dot
cmp ah, " "
je empty
jne letters
letters:
cmp ah, cl
je uppercase
inc cl
cmp cl, ch
jne letters
mov cl, 61h ; a-z
mov ch, 7Ah
lowercase:
inc al
cmp dl,al
jl maksimum
inc bx
jmp check
maksimum:
mov dl, al
inc bx
jmp check
uppercase:
inc bx
jmp check
empty:
mov al, 0
inc bx
jmp check
dot:
My program count lowercases in a word in al. and then puts in dl. (maximum lowercases)
I have label which name is dot. there I have to put some instruction by which I can print my result:
Summer is the word with the most lower cases 5
I try few instructions to do that but it doesnt work.
If you're using Windows, the easiest way is to use DOS Interrupts. Specifically, try interrupt 09. This takes a string and outputs it to the standard output.

Resources