I got a string "Wind" and am iterating through it until the character being pointed at matches "i" and gets exchanged with "a". Now what I want is to every time print out "Wind" and afterwards, if this works, print out the part beginning from the actual pointer to the end of my string. Here is my example code:
DATA SEGMENT
String db "Wind"
db "$"
length equ $ - String
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
mov ax,DATA
mov ds,ax
mov bx,offset String
mov al,length
routine:
lea dx,String
mov ah,9
int 21h
cmp word ptr [bx],"i"
je found
inc bx
dec al
cmp al,0
jne routine
je end
found:
mov word ptr [bx],"a"
jmp end
ende: mov ah,4Ch
int 21h
CODE ENDS
END routine
The problem is, code seems to be working fine at exchanging "i" with "a", but it doesn't print out "Wind" every time the character isn't being found or "Wand", instead it prints out something like "§$%&$§/&§$%&DFSGerwer"§$Wind§$%§46534545".
What am I doing wrong?
lea dx,String
use this instead...
mov dx,offset String
cmp word ptr [bx],"i"
is this what you really want ? that will only be true if the "i" is followed by a 0 byte
try this.....
cmp byte ptr [bx],"i"
same for this line......
mov word ptr [bx],"a"
"a" and "i" are bytes, normally
Also:
Length and end are reserved words.
Routine will not work as an entry point label because the first four instructions are necessary and they will be skipped.
The interrupt call will overwrite AL so you cannot use it as a counter. I suggest using CX instead.
After you DEC the counter there is no need to compare the counter to zero, because DEC will set the zero flag when the counter reaches zero. Something like this would be preferable:
dec cx
jnz routine
the loop instruction will do that for you if the count is in CX
loop routine
your "length" variable includes the $ terminator
that is why you see garbage out - it is being overwritten and when you display the
string, it will display text until it happens to run across a $ in the code or memory remnants
mov cx,lngth-1
routin:
;
;
push cx
int 21h
pop cx
;
;
loop routin
this will pass through the loop "lngth-1" times
although the CX register may be preserved across the INT 21h call, there is no guarantee
it is not documented anywhere that it will be saved for you (even though it is, in this case)
if you want a register preserved, save it yourself to be on the safe side
it will save you many hours of pulling your hair out
there are a few exceptions, of course
Okay, so I changed the code to
DATA SEGMENT
String db "Wind"
db "$"
lngth equ $ - String
DATEN ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
mov ax,DATA
mov ds,ax
mov bx,offset String
mov cx,lngth - 1
routine:
mov dx,offset String
mov ah,9
push cx
int 21h
pop cx
cmp byte ptr [bx],"i"
je found
inc bx
dec cx
jnz routine
je stop
found:
mov byte ptr [bx],"a"
jmp stop
stop: mov ah,4Ch
int 21h
CODE ENDS
END routine
but it still puts garbage on the screen, with "Wind" in between!
I found out that "Wind$" is found in DS:0100h, but somehow the assembler puts 0 into bx instead of 0100h. Strangewise, CX = 0 at entry point and gets decremented to FFFFh.
Does someone know the answer?
QuoteAfter you DEC the counter there is no need to compare the counter to zero, because DEC will set the zero flag when the counter reaches zero. Something like this would be preferable:
dec cx
jnz routine
I did as you said and the assembler changes
JNZ
to
CMP Byte Ptr [BX],69
JZ 001F
,
which are ten bytes and as many as bytes as in
CMP Byte Ptr [BX],69
JZ 0022
,
so there only should be a reading advantage in changing this?
The main reason you are getting garbage is that you are still using the routine label as an entry point, so DS, BX and CX are not being initialized. To ensure that the instructions above the routine label execute, you need to place another label, I suggest start, above the mov ax,DATA statement, and modify your END directive accordingly.
BTW, you could have easily detected this problem, and probably others, by simply assembling, linking, and then testing the EXE with DEBUG.
Thanks for your help in this. Assembling, linking and debugging is exactly where I got my information in my previous post from!
DATA SEGMENT
String db "Wind",0Ah
db "$"
lngth equ $ - String
DATA ENDS
CODE SEGMENT
start:
ASSUME CS:CODE,DS:DATA
mov ax,DATA
mov ds,ax
lea bx,String
mov cx,lngth
routine:
lea dx,[bx]
mov ah,9
push cx
int 21h
pop cx
cmp byte ptr [bx],"i"
je found
inc bx
loop routine
found:
mov byte ptr [bx],"a"
lea dx,String
int 21h
mov ah,4Ch
int 21h
CODE ENDS
END start
is the actual result of this thread.
The output is the following:
Wind
ind
Wand
you can replace the LEA instructions as follows....
;------------------------------------------
DATA SEGMENT
String db "Wind"
lngth equ $ - String
db 0Dh,0Ah,24h
DATA ENDS
;------------------------------------------
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
start:
mov ax,DATA
mov ds,ax
mov bx,offset String
mov cx,lngth
routine:
mov dx,bx
mov ah,9
push bx
push cx
int 21h
pop cx
pop bx
cmp byte ptr [bx],"i"
je found
inc bx
loop routine
found:
mov byte ptr [bx],"a"
mov dx,offset String
mov ah,9
int 21h
mov ax,4C00h
int 21h
CODE ENDS
;------------------------------------------
END start
I see why I should replace it in 'routine', but I don't see why I should do it in 'start' and in 'found'?
lea is "Load Effective Address"
it caclulates the effective address refered to in the source operand and places it in the destination operand
it is used for things like this......
lea si,[bx+100h]
esi will be = ebx+100h
but, in your cases, you have no calculation to make - they are simple offsets
that means you can just use
mov si,bx (in my example)
or
mov bx,offset String
mov is a smaller and faster instruction
notice that "offset String" is really just a 16-bit number
if you were to disassemble it, it would look like this
mov bx,1780h (assuming the address of String is 1780h)
if i wanted to get "offset String+100h" into a register, i would again not need to use LEA
the assembler performs the addition (address of String) + (100h) and creates the code for
mov bx,offset String+100h
mov bx,1880h
however, in the case of "[bx+100h]", the assembler has no idea what value is in bx, so i would have to use LEA
EDIT - did you notice the other changes i made ?
You saved bx on the stack and put 'db 0Dh,0Ah,24h' after you defined the length. How is this possible since the string isn't at it's end after length, but before and why did you save the register?
well - just a good habit to save registers across int 21 calls - not needed always
the 0Dh,0Ah,24h is a carriage return, a line feed, and a "$" string terminator
you had only a line feed, which goes to the next line but does not change the column
carriage return sets the column to 0 for the next character
you will see 0Dh and 0Ah (or 13,10 decimal) together quite often
i moved the "lngth" label so that it would not calculate the string length with the line feed
you just want to run through the loop 4 times, not 5
i also changed mov ah,4Ch to mov ax,4C00h
this is not that critical, but DOS accepts a return code in AL
if the program were run in a batch file, the return code value can be recovered
if you do not set the value in AL, the return code will be whatever value is in AL at the time
it is just good practice to set it to 0, indicating "no error", if you do not use it otherwise
After passing exams and further reading sessions I had a few minutes left to change my code into this:
DATA SEGMENT
S_Question db "Input: ", "$"
introduction db "String before:", 0Ah, "$"
outtro db 0Ah,"String after:", 0Ah, "$"
String db "Wind",0Ah, "$"
lngth equ $ - String
LF db 0Ah,"$"
answer db "1234567890", "$"
DATA ENDS
CODE SEGMENT
start:
ASSUME CS:CODE,DS:DATA
mov ax,DATA
mov ds,ax
mov dx,offset S_Question
call printString
mov dx, offset answer
call readString
mov dx,offset LF
call printString ; line feed for readabillity
call printString ; line feed for readabillity
;mov dx, offset introduction
mov dx, offset answer
call printString
mov bx,offset String
mov cx,lngth
mov dx,bx
call printString
routine:
cmp byte ptr [bx],"i"
je found
inc bx
loop routine
found:
mov dx, offset outtro
call printString
mov byte ptr [bx],"a"
mov dx,offset String
call printString
mov ah,4Ch
int 21h
;*******************************************************
;subfunction for the input of a string
;from keyboard.
;input parameters:
;output parameters:
;changed registers:
;*******************************************************
readString PROC NEAR
mov ah,0Ah
int 21h
ret
readString ENDP
;******************************************************
;subfunction for printing a char array
;which is terminated by "$"-char
;output parameters: none
;changed registers: AH,DX
;******************************************************
printString PROC NEAR
push ax
mov ah,09h
int 21h
pop ax
ret
printString ENDP
CODE ENDS
END start
Actually, I'm trying to expand my code so that the inputted string will be the word in which the char is be to replaced. But I don't know how to overwrite the field answer or string with the input?
I thought the subfunction readString could be of use to me, but it just won't work. Any suggestions?
without looking to hard at your code...
you need to set up an input data buffer
it has been a while since i used DOS line input, as i always wrote my own routines using BIOS INT 16h calls
but, i think a 128 character buffer is adequate
here are two methods of DOS line input...
first method:
INT 21h Function 0Ah:
Read buffered array from standard input
Requires a predefined structure to be set up that describes the maximum input size and holds the input characters.
Example:
count = 80
KEYBOARD STRUCT
maxInput BYTE count ; max chars to input
inputCount BYTE ? ; actual input count
buffer BYTE count DUP(?) ; holds input chars
KEYBOARD ENDS
.data
kybdData KEYBOARD <>
.code
mov ah,0Ah
mov dx,OFFSET kybdData
int 21h
second method:
INT 21h Function 0Bh:
Get status of standard input buffer
L1: mov ah,0Bh ; get buffer status
int 21h
cmp al,0 ; buffer empty?
je L1 ; yes: loop again
mov ah,1 ; no: input the key
int 21h
mov char,al ; and save it
Can be interrupted by Ctrl-Break (^C)
I'm sorry I have to say that your post didn't help me because my lack of intelligence maybe. But I found an example at emu8086 which I expanded to the following procedure:
;******************************************************
; procedure for reading a string from keyboard and
; cut off the control bytes to place it correctly into
; DS:DX.
; input: DS:DX
; output: DS:DX
; changed registers: AX, BX, CX
;******************************************************
readString PROC NEAR
push ax
push bx
push cx
mov ah,0Ah
int 21h
mov di, dx
xor bx,bx
mov bl,[di + 1]
mov byte ptr [di + bx + 2], '$'
mov cx,bx
xor bx,bx
transfer:
mov al, byte ptr [di + 2 + bx]
mov [di + bx], al
inc bx
cmp bx,cx
jle transfer
pop cx
pop bx
pop ax
ret
readString ENDP
You just have to pass the relative adress of the memory you want to store your string into by
mov dx, offset stringIWantToStoreMyInput
before
call readString
.
Explanation:
After
mov ah,0Ah
int 21h
the first two bytes of the stored input are control bytes which the first from carries the length of the input (I don't know what's the second control byte for). Before cutting off the two control bytes I put a '$' at the end of the input to make a string out of it. In transfer finally I cut off the two control bytes by copying byte for byte beginning at the position of the first control byte.
I don't know if this is efficient though.. At least it works!
The first byte of the buffer "specifies the maximum number of characters, including the carriage return, to be copied to the buffer. This value, set by the program, must not exceed 255."
The second byte of the buffer "receives the actual number of characters copied to the buffer, not counting the carriage return. The function sets this value."