I have a problem with a TASM based assembly code. I would like to convert a string in the "string" pointer into a decimal number and then print it as a hexadecimal one. However the code prints only the first 2 characters from the right correctly. What may be wrong with this?
concd SEGMENT
ASSUME cs: concd
ORG 100h
main:
mov ax, 0
mov bx, offset string
mov cx, 0
mov dx, 0
mov si, 0
jump:
mov cx, [bx + si]
cmp cx, 0h ;checking if we reached the end of the string
jz exit
mov dx, ax
mov cx, 9
mult:
add ax, dx ;loop for multiplying by 10
loop mult
mov cx, [bx + si]
sub cx, 30h
add ax, cx
inc si
jmp jump
exit:
call hex
mov ah, 04Ch
mov al, 0
int 21h
hex PROC
mov dx, ax
mov cl, 12
jump2:
mov ax, dx
shr ax, cl
and ax, 15
cmp ax, 9
jng t
add ax, 7h
t: add ax, 30h
call putc
sub cl, 4
jnc jump2
RET
hex ENDP
putc PROC
mov ah, 0Eh
int 10h
RET
putc ENDP
string DB "1234", 0h
concd ENDS
END main
I made some small changes, and now it works.
I commented with "phase 1" the changes that it actually did get a character abd did something, and with "phase 2" the changes that corrected the conversion.
Now it's OP's time to figure out what was wrong.
concd SEGMENT
ASSUME cs: concd
ORG 100h
main:
mov ax, cs ;; added - phase 1
mov ds, ax ;; added - phase 1
mov ax, 0
mov bx, offset string
mov cx, 0
mov dx, 0
mov si, 0
jump:
mov ch, 0 ;; Added - phase 2
mov cl, [bx + si] ;; added - phase 2
;mov cx, [bx + si] ;; removed - phase 2
cmp cx, 0h ;checking if we reached the end of the string
jz exit
mov dx, ax
mov cx, 9
mult: ; ax contains the number
;; removed the next 2 lines - phase 2
;add ax, dx ;loop for multiplying by 10
;loop mult
;; added the next 2 lines - phase 2
mov cl, 10
mul cl ; multiply by 10
mov cx, [bx + si]
sub cx, 30h
add ax, cx
inc si
jmp jump
exit:
call hex
mov ah, 04Ch
mov al, 0
int 21h
hex PROC
mov dx, ax
mov cl, 12
jump2:
mov ax, dx
shr ax, cl
and ax, 15
cmp ax, 9
jng t
add ax, 7h
t: add ax, 30h
call putc
sub cl, 4
jnc jump2
RET
hex ENDP
putc PROC
mov ah, 0Eh
int 10h
RET
putc ENDP
string DB "1234", 0h
concd ENDS
END main
Related
I made a program that reads a string and converts it into a number. A string representation of the number entered in the decimal system. The result of of converting is in register ax.
I use tasm16.
How do I do that I can convert a string bigger(more than 65535)?
model small
stack 100h
printstring macro msg
mov ah,09h
mov dx,offset msg
int 21h
endm
data segment
cr equ 0dh
lf equ 0ah
errorInput db 'Wrong input',cr,lf,'$'
inputAmsg db 'Please input A',cr,lf,'$'
outstring db 'Output:',cr,lf,'$'
maxAbuf db 10
db ?
db 10 dup(0)
db '$'
result dw 0
data ends
code segment
assume cs:code, ds:data
start:
mov ax,data
mov ds,ax
;input A
printstring inputAmsg
mov ah,0ah
lea dx,maxAbuf
int 21h
printstring outstring
mov ah,09h
lea dx,maxAbuf+2
int 21h
;in:
;dx:address of string
;out
;dx:ax - result ;work only ax
;
call Convert
jmp exit
;in:
;dx:address of string
;out
;dx:ax - result
;9999989d = 989675h
Convert proc
xor ax,ax
xor cx,cx
mov bx,10 ;base
mov si, offset dx
xor dx,dx
getnewchar: mov cl,[si]
cmp cl,0dh ;if `$` exit
jz endproc
sub cl,'0' ;or 30h
mul bx
add ax,cx
;I do not know what to write here, if there is an exit for the bit grid.
adc dx,0
inc si
jmp getnewchar
endproc:
ret
endp
exit: mov ax,4c00h
int 21h
code ends
end start
You could, for example, extend your multiplication by 10 from 16 to 32 bits like this:
; file: mul32ten.asm
; compile: nasm.exe mul32ten.asm -f bin -o mul32ten.com
bits 16
org 0x100
xor dx, dx
xor ax, ax
mov si, MyStr
NextDigit:
mov bl, [si]
or bl, bl
je Done
call MulDxAxBy10
sub bl, '0'
add al, bl
adc ah, 0
adc dx, 0
inc si
jmp NextDigit
Done:
; dx:ax should be equal to 7FFFFFFF now
ret
MulDxAxBy10:
push si
push di
shld dx, ax, 1
shl ax, 1 ; dx:ax = n * 2
mov si, ax
mov di, dx ; di:si = n * 2
shld dx, ax, 2
shl ax, 2 ; dx:ax = n * 8
add ax, si
adc dx, di ; dx:ax = n * 8 + n * 2 = n * (8 + 2) = n * 10
pop di
pop si
ret
MyStr db "2147483647", 0
So I'm writing a uni assignment - a program that subtracts two entered decimals (max 10 characters ea). The first iteration works as intended. However, when I restart the program, for some reason the second operand is remembered.
The prompt to enter it does come up, but is then skipped as if I've entered something already - the same thing I entered the first iteration, in fact.
The question is: why does it happen and how do I fix it? The first prompt works correctly.
The prompt is under INPUT_2:
.model small
.386
stack 100h
dataseg
inputMsg1 db 0Ah, 0Dh, 'Enter first operand', 0Ah, 0Dh, '$'
inputMsg2 db 0Ah, 0Dh, 'Enter second operand', 0Ah, 0Dh, '$'
inputMax1 db 11
inputLen1 db ?
input1 db 12 dup(?)
input1Packd db 5 dup(0)
inputMax2 db 11
inputLen2 db ?
input2 db 12 dup(?)
input2Packd db 5 dup(0)
packMode db 0 ;Режим упаковки: 1 - первая цифра, 2 - вторая
resMsg db 0Ah, 0Dh, 'Result: $'
res db 9 dup(' '),'$'
retryMsg db 0Ah, 0Dh
db 'Press Any Key to continue, ESC to quit'
db '$'
errorMsg db 0Ah, 0Dh, 'Something went wrong. Try again$'
codeseg
START:
startupcode
jmp INPUT_1
INPUT_1_ERROR:
lea DX, errorMsg
mov AH, 09h
int 21h
INPUT_1:
lea DX, inputMsg1
mov AH, 09h
int 21h
lea DX, inputMax1
mov AH, 0Ah
int 21h
cmp inputLen1, 0
jz INPUT_1_ERROR
INPUT_1_PROCESS:
lea BX, input1
lea DX, input1Packd
xor CX, CX
mov CL, inputLen1
mov SI, CX
dec SI
mov DI, 4
INPUT_1_LOOP:
mov AL, [BX][SI]
cmp AL, '0'
jb INPUT_1_ERROR
cmp AL, '9'
ja INPUT_1_ERROR
and AL, 0Fh
mov AH, packMode
cmp AH, 0
jnz INPUT_1_PACK_SECOND
INPUT_1_PACK_FIRST:
inc AH
push BX
mov BX, DX
mov [BX][DI], AL
pop BX
jmp INPUT_1_PACK_FINISHED
INPUT_1_PACK_SECOND:
dec AH
shl AL, 4
push BX
mov BX, DX
or [BX][DI], AL
pop BX
dec DI
INPUT_1_PACK_FINISHED:
mov packMode, AH
dec SI
loop INPUT_1_LOOP
mov packMode, 0
jmp INPUT_2
INPUT_2_ERROR:
lea DX, errorMsg
mov AH, 09h
int 21h
INPUT_2:
lea DX, inputMsg2
mov AH, 09h
int 21h
lea DX, inputMax2
mov AH, 0Ah
int 21h
cmp inputLen2, 0
jz INPUT_2_ERROR
INPUT_2_PROCESS:
lea BX, input2
lea DX, input2Packd
xor CX, CX
mov CL, inputLen2
mov SI, CX
dec SI
mov DI, 4
INPUT_2_LOOP:
mov AL, [BX][SI]
cmp AL, '0'
jb INPUT_2_ERROR
cmp AL, '9'
ja INPUT_2_ERROR
and AL, 0Fh
mov AH, packMode
cmp AH, 0
jnz INPUT_2_PACK_SECOND
INPUT_2_PACK_FIRST:
inc AH
push BX
mov BX, DX
mov [BX][DI], AL
pop BX
jmp INPUT_2_PACK_FINISHED
INPUT_2_PACK_SECOND:
dec AH
shl AL, 4
push BX
mov BX, DX
or [BX][DI], AL
pop BX
dec DI
INPUT_2_PACK_FINISHED:
mov packMode, AH
dec SI
loop INPUT_2_LOOP
MATH_SETUP:
mov SI, 4
mov CX, 4
mov DI, 7
MATH:
lea BX, input1Packd
mov AL, [BX][SI]
lea BX, input2Packd
mov AH, [BX][SI]
sbb AL, AH
pushf
das
dec SI
mov AH, AL
lea BX, res
and AL, 0Fh
or AL, 30h
mov [BX][DI], AL
dec DI
shr AH, 4
or AH, 30h
mov [BX][DI], AH
dec DI
popf
loop MATH
lea BX, res
mov CX, 7
mov SI, 0
SHORTEN:
mov AL, [BX][SI]
cmp AL, '0'
jnz WRAPUP
inc SI
loop SHORTEN
WRAPUP:
push CX
lea DX, resMsg
mov AH, 09h
int 21h
lea DX, res
pop CX
cmp CX, 0
jz SKIP_SHORTEN
PRINT_SHORTEN:
add DX, 7
sub DX, CX
jmp FINISH_SHORTEN
SKIP_SHORTEN:
add DX, 6
FINISH_SHORTEN:
mov AH, 09h
int 21h
lea DX, retryMsg
mov AH, 09h
int 21h
mov AH, 01h
int 21h
cmp AL, 1Bh
jz QUIT
lea BX, input1Packd
mov DX, 1
BCD_CLEANUP:
mov DI, 0
mov CX, 5
BCD_CLEANUP_LOOP:
mov [BX][DI], 0
inc DI
loop BCD_CLEANUP_LOOP
lea BX, input2Packd
cmp DX, 1
mov DX, 0
jz BCD_CLEANUP
jmp START
QUIT:
exitcode 0
end START
Any better code suggestions welcome as well, but not needed if you don't have an answer for the question.
The DOS buffered input function 0Ah allows you to have a preset text in the storage space of the input buffer that you provide. For a complete explanation of this DOS function see how buffered input works.
The first time that your program runs the inputLen1 and inputLen2 fields are empty because of how you defined them in the source using db ? which translates to zero.
But when you re-run the code this is no longer the case! The lengths still show what you got in the previous run. You need to zero both these fields before invoking function 0Ah again on the same input buffers.
mov DX, 0
jz BCD_CLEANUP
mov inputLen1, DL ;DL=0
mov inputLen2, DL ;DL=0
jmp START
The MATH loop has a couple of problems regarding the CF.
The sbb al, ah instruction depends on the value in the carry flag, but you neglect to make sure it is off on the first iteration of this loop. Just add clc:
clc
MATH:
lea BX, input1Packd
mov AL, [BX][SI]
lea BX, input2Packd
mov AH, [BX][SI]
sbb AL, AH
The das instruction consumes the carry flag that you get from the sbb instruction, but it's the carry flag that you get from the das instruction that you need to preserve/restore to have it propagate through the loop.
sbb AL, AH
das
pushf
...a program that subtracts two entered decimals (max 10 characters ea)...
If ever you enter 9 or 10 characters, those most significant digits will not be taken into account because MATH_SETUP really limits you to 8 characters (which in turn is a good thing since the res buffer only has room to show 8 characters)!
MATH_SETUP:
mov SI, 4 <-- Could permit 10 packed BCD digits
mov CX, 4 <-- Max 8 characters
mov DI, 7 <-- Max 8 characters
In a program I am currently doing, I have to reverse a string a user has entered. I have to leave the word that the user entered in where I prompted them to enter it and right below it I want to print out the word in reverse. When I try to run it in DOSBox with the Tasm compiler it gives me an error that says "Illegal memory reference" on line 189 which is the line that contains the variable I plan to put the reversed word in. Can someone help me find out what I am doing wrong? I would greatly appreciate it! Also only in my program there are 4 boxes. The first box I try to print the reverse word below the prompt. The rest of the boxes prints the user entered word instead of the reversed version of it.
title fill in title ;program name
;------------------------------------------------------------------
stacksg segment para stack 'Stack' ;define the stack
db 32 dup(0) ;32 bytes, might want larger
stacksg ends
;------------------------------------------------------------------
datasg segment para 'Data' ;data segment
paralst Label Byte
maxlen db 21
actlen db ?
dbdata db 21 dup('$')
outit db 'Enter String: $' ;14 chars minus $
switch db 21 dup('$')
datasg ends
;------------------------------------------------------------------
codesg segment para 'Code' ;code segment
main proc far ;main procedure
assume ss:stacksg, ds:datasg, cs:codesg ;define segment registers
mov ax, datasg ;initialize data segment register
mov ds, ax
;--------------- --------------------------top left corner
mov ah, 06h
mov al, 00
mov bh, 01000001b ; 4eh
mov ch, 0
mov cl, 0
mov dl, 39
mov dh, 12
int 10h
;-------------------------------------------top right corner
mov ah, 06h
mov al, 0
mov bh, 11110010b
;mov cx, 0c00h
;mov dx, 184fh
mov ch, 0
mov cl, 39
mov dh, 12
mov dl, 79
int 10h
;--------------------------------------------bottom left corner
mov ah, 06h
mov al, 0
mov bh, 11100100b ;yellow
mov ch, 12
mov cl, 0
mov dh, 24
mov dl, 39
int 10h
;------------------------------------------bottom right corner
mov ah, 06h
mov al, 0
mov bh, 01011111b ; magenta 80
mov ch, 12
mov cl, 39
mov dh, 24
mov dl, 79
int 10h
;--------------------------------------------------- 1st quad
mov ah, 02h
mov bh, 0
mov dh, 5
mov dl, 5
int 10h
mov ah, 09h
lea dx, outit
int 21h
;------------------------------------input
mov ah, 0ah
lea dx, paralst
int 21h
; -----------------------------------move cursor
mov ah, 02h
mov bh, 0
mov dh, 7
mov dl, 5
int 10h
call REVERSE
;--------------------------------------print output
mov ah, 09h
lea dx, switch
int 21h
;----------------------------------------------------2nd quad
mov ah, 02h
mov bh, 0
mov dh, 5
mov dl, 44
int 10h
mov ah, 09h
lea dx, outit
int 21h
;------------------------------------input
mov ah, 0ah
lea dx, paralst
int 21h
; -----------------------------------move cursor
mov ah, 02h
mov bh, 0
mov dh, 7
mov dl, 44
int 10h
;--------------------------------------print output
mov ah, 09h
lea dx, dbdata
int 21h
;------------------------------------------------------3rd quad
mov ah, 02h
mov bh, 0
mov dh, 17
mov dl, 5
int 10h
mov ah, 09h
lea dx, outit
int 21h
;------------------------------------input
mov ah, 0ah
lea dx, paralst
int 21h
; -----------------------------------move cursor
mov ah, 02h
mov bh, 0
mov dh, 19
mov dl, 5
int 10h
;--------------------------------------print output
mov ah, 09h
lea dx, dbdata
int 21h
;------------------------------------------------------4th quad
mov ah, 02h
mov bh, 0
mov dh, 17
mov dl, 44
int 10h
mov ah, 09h
lea dx, outit
int 21h
;------------------------------------input
mov ah, 0ah
lea dx, paralst
int 21h
; -----------------------------------move cursor
mov ah, 02h
mov bh, 0
mov dh, 19
mov dl, 44
int 10h
;--------------------------------------print output
mov ah, 09h
lea dx, dbdata
int 21h
mov ax, 4c00h ;end processing
int 21h
main endp ;end of procedure
;----------------------------------------reverse procedure
REVERSE PROC NEAR
mov cx, 0
;-----figure out actlen here
mov actlen, 0
lea bx, dbdata ;may need to use paralst instead
hi: cmp [bx], '$'
jne sup
inc actlen
inc bx
jmp hi
sup:
;------------
mov cx, 0
mov cl, actlen
lea bx, dbdata
add bx, cx
yo: cmp actlen, 0
je hola
mov switch, byte ptr[bx]
dec bx
inc switch
dec actlen
jmp yo
hola:
RET
REVERSE ENDP
codesg ends ;end of code segment
end main ;end of program
You can't do mov memory, memory. Move the source into a register first, then place it at the destination.Also, inc switch doesn't do what you seem to think it's doing. You can't change the address of switch at runtime, so what's actually happening there is that you're incrementing the first element at switch (as if you had written inc [switch]). If you need to modify an address; again, put it in a register.
So for example:
lea si, dbdata
add si,cx
lea di, switch
mov al,[bx]
jcxz hola ; jump if cx == 0
cld ; clear the direction flag; for stosb
yo: mov al,[si]
stosb ; [di++] = al
dec si
loop yo ; if (--cx) jmp yo
hola:
I didn't look at the entire piece of code, so it's unclear to me whether your reverse is supposed to copy the terminator ('$') or not. If it should, you should always execute the loop at least once (and then you don't need the jcxz). If it shouldn't, you should set the source address to dbdata + cx - 1 instead of dbdata + cx.
I don't know much of assembly language but I tried making this palindrome and it's quite hard. First I have to enter a string then show its original and reversed string then show if its a palindrome or not.
I already figured out how to show the reverse string by pushing and popping it through a loop from what I have read in another forum and figured it out
now the only problem for me is to compare the reverse string and original string to check if its a palindrome or not.
call clearscreen
mov dh, 0
mov dl, 0
call cursor
mov ah, 09h
mov dx, offset str1
int 21h
palin db 40 dup(?)
mov ah, 0ah
mov palin, 40
mov dx, offset palin
int 21h
mov dh, 1
mov dl, 0
call cursor
mov ah, 09h
mov dx, offset str2
int 21h
mov si, 2
forward:
mov al, palin + si
cmp al, 13
je palindrome
mov ah, 02h
mov dl, al
push ax 'push the letter to reverse
int 21h
inc si
jmp forward
palindrome:
mov dh, 2
mov dl, 0
call cursor
mov ah, 09h
mov dx, offset str3
int 21h
mov cx, 40 'pop each letter through a loop to show its reverse
reverse:
mov ah, 02h
pop ax
mov dl, al
int 21h
loop reverse
int 20h
clearscreen:
mov ax, 0600h
mov bh, 0Eh
mov cx, 0
mov dx, 8025
int 10h
ret
cursor:
mov ah, 02h
mov bh, 0
int 10h
ret
str1: db "Enter A String : $"
str2: db "Forward : $"
str3: db "Backward : $"
str4: db "Its a Palindrome! $"
str5: db "Not a Palindrome!$"
You have a string the user typed in, what you need to do is compare the first byte with the last byte, do the same for the 2nd one and the 2nd to last one. Keep doing this for the whole string. You also need the length of the string. To make life easier, you should convert the string to all UPPER case letters or all lowercase letters to make comparison easier.
It is unfortunate they are still teaching 16bit DOS code. This is a sample with the word defined in the data section. You will have to modify it to receive input and work on that string
.data
pal db "racecar"
pal_len equ $ - pal - 1
szYes db "yes$"
szNo db "no$"
.code
start:
mov ax,#data
mov ds,ax
call IsPalindrome
mov ah,4ch
int 21h
IsPalindrome:
lea si, pal
lea di, pal
add di, pal_len
mov cx, 0
CheckIt:
mov al, byte ptr [si]
mov dl, byte ptr [di]
cmp al, dl
jne No
inc si
dec di
inc cx
cmp cx, pal_len
jne CheckIt
mov ah,9
lea dx,szYes
int 21h
ret
No:
mov ah,9
lea dx,szNo
int 21h
ret
end start
For completeness and to bring us into the 21st century, 32bit NASM code:
section .data
fmt db "%s", 0
szPal db "RACECAR"
Pal_len equ $ - szPal - 1
szYes db "Yes", 10, 0
szNo db "No", 10, 0
extern printf, exit
global _start
section .text
_start:
call IsPalindrome
call exit
IsPalindrome:
mov ecx, 0
mov ebx, Pal_len
mov esi, szPal
.CheckIt:
mov al, byte [esi + ecx]
mov dl, byte [esi + ebx]
cmp al, dl
jne .No
inc ecx
dec ebx
jns .CheckIt
push szYes
push fmt
call printf
add esp, 4 * 2
mov eax, 1
jmp Done
.No:
push szNo
push fmt
call printf
add esp, 4 * 2
xor eax, eax
Done:
ret
I have a small problem.
This is my code:
cmpstr:
pusha
xor cx, cx
mov ax, ds
push ax
mov ax, si
push ax
call strlen
mov dx, ax
mov ax, es
mov ds, ax
mov si, bx
call strlen
cmp al, dl
jnz .fail
pop ax
mov si, ax
pop ax
mov ds, ax
.loop:
push bx
mov al, byte [es:bx]
mov bl, byte [ds:si]
cmp al, bl
jne .fail
cmp bl, 0
jz .suc
pop bx
add bx, 1
inc si
inc cx
jmp .loop
.fail:
mov al, 'C';
mov ah, 0Eh
int 10h
popa
mov ax, 0
jmp .end
.suc:
mov al, 'D';
mov ah, 0Eh
int 10h
popa
mov ax, 1
.end:
ret
This procedure should compare two strings and return (mov to ax) 1, if strings (first on es:bx, second ds:si) are same or 0 if they're different. My problem is that command before procedure call is executed, letter 'D' is being printed (comparasion was successful) but command after procedure call is not working. I think that problem must be somewhere in this procedure. Does anybody know what is wrong here?
When you leave the loop with one of the conditional jumps
.loop:
push bx
mov al, byte [es:bx]
mov bl, byte [ds:si]
cmp al, bl
jne .fail
cmp bl, 0
jz .suc
You have pushed BX, but you never pop it. That will make the next RET go to strange places.