Error in a string reversal program in emu8086 - string

I tried a string reversal program, but emu8086 is showing me an error message
(12)offset calculation error
What is wrong with the line LEA DI,STR2+LEN-1 ?
DATA SEGMENT
STR1 DB 'HELLO'
LEN EQU $-STR1
STR2 DB 20 DUP(0)
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE, DS:DATA, ES:DATA
START: MOV AX,DATA
MOV DS,AX
MOV ES,AX
LEA SI,STR1
LEA DI,STR2+LEN-1
MOV CX,LEN
UP: CLD
LODSB
STD
STOSB
LOOP UP
MOV AH,4CH
INT 21H
CODE ENDS
END START

I'm not using emu8086, but the following will correct your line 12:
mov di, offset STR2 + LEN - 1
The shorter way to obtain an address is writing mov si, offset STR1 (uses 3 bytes) instead of lea si, STR1 (uses 4 bytes).
The loop ends with the direction flag still set!
It will always be a good idea to have the direction flag cleared before invoking any system function:
...
LOOP UP
CLD ; Add this to clear direction flag
MOV AH,4CH ; DOS.Terminate
INT 21H
...

Related

Intel 8086 assembly reversing order of string

So I'm programming code in Intel 8086 assembly where I want to input two strings(one string in one line) and a want to save them to the variables. Each string can contain up to 100 utf8 characters. In the output, I try to change the order of then (the first line is going to be the second string from the input) but I get an error that the service 21h is trying to read from an undefined byte. Can you explain to me what I Should change in my code?
cpu 8086
segment code
..start mov ax, data
mov ds, ax
mov bx,stack
mov ss,bx
mov sp,dno
input1 mov ah,0x0a
mov dx, load
int 21h
mov bl,[load+1]
mov [chars1+bx],byte '$'
input2 mov ah,0x0a
mov dx, load
int 21h
mov bl,[load+1]
mov [chars2+bx],byte '$'
output mov dx,chars2
mov ah,9
int 21h
mov dx,chars1
mov ah,9
int 21h
end hlt
segment data
load db 200, ?
chars1 db ?
resb 100
chars2 db ?
resb 100
segment stack
resb 100
dno: db ?
but I get an error that the service 21h is trying to read from an undefined byte. Can you explain to me what I Should change in my code?
You are not loading the address of the input structure that is required by the DOS.BufferedInput function 0Ah. Read all about it in How buffered input works.
Emu8086 follows MASM programming style where mov dx, load will set the DX register equal to the word stored at the address load. To actually receive the address itself, you'll need to write mov dx, offset load.
A further problem is that both input1 and input2 try to use the same load input structure but with different buffer memories. The buffer has to be part of the input structure for this DOS function; it always has to reside at offset 2 from the provided address (load in your case).
This is how you can solve it:
...
input1: mov ah, 0Ah
mov dx, OFFSET loadA
int 21h
mov bl, [loadA+1]
mov bh, 0
mov [charsA+bx], byte '$'
input2: mov ah, 0Ah
mov dx, OFFSET loadB
int 21h
mov bl, [loadB+1]
mov bh, 0
mov [charsB+bx], byte '$'
output: mov dx, OFFSET charsB
mov ah, 09h
int 21h
mov dx, OFFSET crlf
mov ah, 09h
int 21h
mov dx, OFFSET charsA
mov ah, 09h
int 21h
...
loadA db 101, 0
charsA db 101 dup (0)
loadB db 101, 0
charsB db 101 dup (0)
crlf db 13, 10, "$"
...
101 provides room for 100 characters and 1 terminating carriage return.
Don't trust too much registers that you didn't set yourself! Write an explicite mov bh, 0 before using the whole of BX.
Since you want these strings outputted on different lines, I've added code to move to the start of the following line. See the crlf.

Getting string input and displaying input with DOS interrupts MASM

In MASM, I created a buffer variable to hold the user string input from keyboard. I am stuck on how to hold the string input into that buffer variable. I don't have any libraries linked like the irvine ones and want to do this with DOS interrupts. So far I have something along the lines of
.model small
.stack 100h
.data
buff db 25 dup(0), 10, 13
lbuff EQU ($ - buff) ; bytes in a string
.code
main:
mov ax, #data
mov ds, ax
mov ah, 0Ah ; doesn't work
mov buff, ah ; doesn't seem right
int 21h
mov ax, 4000h ; display to screen
mov bx, 1
mov cx, lbuff
mov dx, OFFSET buff
int 21h
mov ah, 4ch
int 21h
end main
I assume using 0Ah is correct as it is for reading array of input of buffered characters.
I made some changes to your code. First, the "buff" variable needs the three level format (max number of characters allowed, another byte for the number of characteres entered, and the buffer itself) because that's what service 0AH requires. To use service 0AH I added "offset buff" (as Wolfgang said). Here it is:
.model small
.stack 100h
.data
buff db 26 ;MAX NUMBER OF CHARACTERS ALLOWED (25).
db ? ;NUMBER OF CHARACTERS ENTERED BY USER.
db 26 dup(0) ;CHARACTERS ENTERED BY USER.
.code
main:
mov ax, #data
mov ds, ax
;CAPTURE STRING FROM KEYBOARD.
mov ah, 0Ah ;SERVICE TO CAPTURE STRING FROM KEYBOARD.
mov dx, offset buff
int 21h
;CHANGE CHR(13) BY '$'.
mov si, offset buff + 1 ;NUMBER OF CHARACTERS ENTERED.
mov cl, [ si ] ;MOVE LENGTH TO CL.
mov ch, 0 ;CLEAR CH TO USE CX.
inc cx ;TO REACH CHR(13).
add si, cx ;NOW SI POINTS TO CHR(13).
mov al, '$'
mov [ si ], al ;REPLACE CHR(13) BY '$'.
;DISPLAY STRING.
mov ah, 9 ;SERVICE TO DISPLAY STRING.
mov dx, offset buff + 2 ;MUST END WITH '$'.
int 21h
mov ah, 4ch
int 21h
end main
When 0AH captures the string from keyboard, it ends with ENTER (character 13), that's why, if you want to capture 25 characters, you must specify 26.
To know how many characters the user entered (length), access the second byte (offset buff + 1). The ENTER is not included, so, if user types 8 characters and ENTER, this second byte will contain the number 8, not 9.
The entered characters start at offset buff + 2, and they end when character 13 appears. We use this to add the length to buff+2 + 1 to replace chr(13) by '$'. Now we can display the string.
This is my code,maybe can help you.
;Input String Copy output
dataarea segment
BUFFER db 81
db ?
STRING DB 81 DUP(?)
STR1 DB 10,13,'$'
dataarea ends
extra segment
MESS1 DB 'After Copy',10,13,'$'
MESS2 DB 81 DUP(?)
extra ends
code segment
main proc far
assume cs:code,ds:dataarea,es:extra
start:
push ds
sub ax,ax
push ax
mov ax,dataarea
mov ds,ax
mov ax,extra
mov es,ax
lea dx,BUFFER
mov ah,0ah
int 21h
lea si,STRING
lea di,MESS2
mov ch,0
mov cl,BUFFER+1
cld
rep movsb
mov al,'$'
mov es:[di],al
lea dx,STR1 ;to next line
mov ah,09h
int 21h
push es
pop ds
lea dx,MESS1 ;output:after copy
mov ah,09h
int 21h
lea dx,MESS2
mov ah,09h
int 21h
ret
main endp
code ends
end start
And the result is:
c:\demo.exe
Hello World!
After Copy
Hello World!
You may follow this code :
; Problem : input array from user
.MODEL SMALL
.STACK
.DATA
ARR DB 10 DUB (?)
.CODE
MAIN PROC
MOV AX, #DATA
MOV DS, AX
XOR BX, BX
MOV CX, 5
FOR:
MOV AH, 1
INT 21H
MOV ARR[BX], AL
INC BX
LOOP FOR
XOR BX, BX
MOV CX, 5
PRINT:
MOV AX, ARR[BX] ;point to the current index
MOV AH, 2 ;output
MOV DL, AX
INT 21H
INC BX ;move pointer to the next element
LOOP PRINT ;loop until done
MAIN ENDP
;try this one, it takes a 10 character string input from user and displays it after in this manner, "Hello *10character string input"
.MODEL TINY
.CODE
.286
ORG 100h
START:
MOV DX, OFFSET BUFFER
MOV AH, 0ah
INT 21h
JMP PRINT
BUFFER DB 10,?, 10 dup(' ')
PRINT:
MOV AH, 02
MOV DL, 0ah
INT 21h
MOV AH, 9
MOV DX, OFFSET M1
INT 21h
XOR BX, BX
MOV BL, BUFFER[1]
MOV BUFFER [BX+2], '$'
MOV DX, OFFSET BUFFER +2
MOV AH, 9
INT 21h
M1: db 'Hello $'
END START
END

Conditional jump fails in linux x86 intel syntax(NASM)

STORY(IM A NEWBIE):
I started reading a pdf tutorial about programming in assembly(x86 intel) using the famous nasm assembler and i have a problem executing a very basic assembly code(inspired by a code about loops from the tutorial).
THE PROBLEM(JE FAILS):
This assembly code should read a digit(a character(that means '0'+digit)) from stdin and then write to the screen digit times "Hello world\n".Really easy loop :decrease digit and if digit equals zero('0' not the integer the character) jump(je) to the exit(mov eax,1\nint 0x80).
Sounds really easy but when i try to execute the output is weird.(really weird and BIG)
It runs many times throught the loop and stops when digit equals '0'(weird because until the program stops the condition digit == '0' been tested many times and it should be true)
Actually my problem is that the code fails to jump when digit == '0'
THE CODE(IS BIG):
segment .text
global _start
_start:
;Print 'Input a digit:'.
mov eax,4
mov ebx,1
mov ecx,msg1
mov edx,len1
int 0x80
;Input the digit.
mov eax,3
mov ebx,0
mov ecx,dig
mov edx,2
int 0x80
;Mov the first byte(the digit) in the ecx register.
;mov ecx,0
mov ecx,[dig]
;Use ecx to loop dig[0]-'0' times.
loop:
mov [dig],ecx
mov eax,4
mov ebx,1
mov ecx,dig
mov edx,1
int 0x80
mov eax,4
mov ebx,1
mov ecx,Hello
mov edx,Hellolen
int 0x80
;For some debuging (make the loop stop until return pressed)
;mov eax,3
;mov ebx,0
;mov ecx,some
;mov edx,2
;int 0x80
;Just move dig[0](some like character '4' or '7') to ecx register and compare ecx with character '0'.
mov ecx,[dig]
dec ecx
cmp ecx,'0'
;If comparison says ecx and '0' are equal jump to exit(to end the loop)
je exit
;If not jump back to loop
jmp loop
;Other stuff ...(like an exit procedure and a data(data,bss) segment)
exit:
mov eax,1
int 0x80
segment .data
msg1 db "Input a digit:"
len1 equ $-msg1
Hello db ":Hello world",0xa
Hellolen equ $-Hello
segment .bss
dig resb 2
some resb 2
THE OUTPUT:
Input a digit:4
4:Hello world
3:Hello world
2:Hello world
1:Hello world
0:Hello world
...
...(many loops later)
...
5:Hello world
4:Hello world
3:Hello world
2:Hello world
1:Hello world
$
That is my question:What is wrong with this code?
Could you explain that ?
AND i dont need alternative codes that will magically(without explanation) run cause i try to learn(im a newbie)
That is my problem(and my first question in Stackoverflow.com )
ECX is 32 bit, a character is just 8 bit. Use a 8 bit register, such as CL instead of ECX.
As jester mentioned, ecx comes in as a character so you probably should use cl
loop:
mov [dig],cl
...
mov cl,[dig]
dec cl
cmp cl,'0'
jne loop
You can also load ecx with movzx which clears the top bits of the register (i.e. a zero-extedning load):
...
movzx ecx, byte [dig]
loop:
mov [dig], cl ; store just the low byte, if you want to store
...
movzx ecx, byte [dig]
dec ecx
cmp ecx, '0'
jne loop
Note that it is often suggested that you do not use the al, bl, cl, dl registers as their use is not fully optimized. Whether this is still true, I do not know.

Sorting strings in 8086 Assembly

I want to write a 8086 assembly program that takes 5 strings from the user as an input and then sorts these strings and prints the sorted result as an output. I actually do everything but I have a big problem with the sorting part. I know how to use a for example bubble sort to sort the items in an array that start from a specific address but here I have 5 different strings that are not in the same array. each string has its own address and its own characters. I try to compare last character of each string with each other and then if one is bigger that another one i swap the whole string and then I go on and do that for the whole characters of all string to the first.
For example if our input strings are:
eab
abe
cbd
cda
adb
I will first sort the last character of every string and I come up with this:
cda
eab
adb
cbd
abe
Then I will compare them by the middle character:
eab
cbd
abe
cda
adb
and at last with the first character and everything is sorted:
abe
adb
cbd
cda
eab
but it is actually what in my mind and I don't have any idea who to implement that for my job.
; multi-segment executable file template.
data segment
data1 db 64,?,64 dup(?)
data2 db 64,?,64 dup(?)
data3 db 64,?,64 dup(?)
data4 db 64,?,64 dup(?)
data5 db 64,?,64 dup(?)
change db 66 dup(?)
msg db 0ah,0dh,"You enter a wrong option",0ah,0dh,"try again",0ah,0dh,"$"
prompt db 0ah,0dh,"Choose an option:",0ah,0dh,"$"
prompt1 db ".a: Sort in ascending order",0ah,0dh,"$"
prompt2 db ".d: Sort in descending order",0ah,0dh,"$"
prompt3 db ".q: Quit",0ah,0ah,0dh,"$"
enter db 0ah,0ah,0dh,"Enter 5 strings:",0ah,0dh,"$"
pkey db 0ah,0dh,"press any key...$"
ends
stack segment
dw 128 dup(0)
ends
code segment
main proc far
; set segment registers:
mov ax, data
mov ds, ax
mov es, ax
again:
; printing the prompts for the user
lea dx, prompt
mov ah, 09h
int 21h
lea dx, prompt1
mov ah, 09h
int 21h
lea dx, prompt2
mov ah, 09h
int 21h
lea dx, prompt3
mov ah, 09h
int 21h
; getting a character from the user as an input
mov ah, 01h
int 21h
; determining which option the user selects
cmp al, 'a'
je ascending
cmp al, 'd'
je descending
cmp al, 'q'
je quit
; this is for the time that the user enters a wrong char
lea dx, msg
mov ah, 09h
int 21h
jmp again ; again calling the application to start
ascending:
call input
call AscendSort
jmp again ; again calling the application to start
descending:
call input
call DescendSort
jmp again ; again calling the application to start
quit:
lea dx, pkey
mov ah, 9
int 21h ; output string at ds:dx
; wait for any key....
mov ah, 1
int 21h
mov ax, 4c00h ; exit to operating system.
int 21h
main endp
;.................................................
; this subroutine gets input from user
input proc
lea dx, enter
mov ah, 09h
int 21h
call newline
mov ah, 0ah
lea dx, data1
int 21h
call newline
mov ah, 0ah
lea dx, data2
int 21h
call newline
mov ah, 0ah
lea dx, data3
int 21h
call newline
mov ah, 0ah
lea dx, data4
int 21h
call newline
mov ah, 0ah
lea dx, data2
int 21h
call newline
ret
input endp
;................................................
; sorting the strings in the ascending order
AscendSort proc
mov si, 65
lea dx, change
mov al, data1[si]
cmp al, data2[si]
ja l1
?????
ret
AscendSort endp
;................................................
; sorting the strings in the descending order
DescendSort proc
ret
DescendSort endp
;................................................
; newline
newline proc
mov ah, 02h
mov dl, 0ah
int 21h
mov dl, 0dh
int 21h
ret
newline endp
ends
end main ; set entry point and stop the assembler.
Any other algorithm for sorting these whole strings also will be appreciated.
I actually figure out the answer myself, I use string commands to compare the strings 2 by 2 with each other to see if they're bigger, smaller or equal. Something like the code below in the specific macro that takes two strings to check them and do the required operation like swapping the strings to make them sorted:
check macro a, b
local next, finish
cld
mov cx, 64 ; the size of our buffer that saves the string
mov si, a
mov di, b
repe cmpsb ; comparing two strings with each other
ja next
jmp finish
next:
; swaping our strings if needed
mov cx, 64
mov si, a
lea di, change
rep movsb
mov cx, 64
mov si, b
mov di, a
rep movsb
mov cx, 64
lea si, change
mov di, b
rep movsb
finish:
endm

Clearing out a string variable

I have writen this little experiement bootstrap that has a getline and print_string "functions". The boot stuff is taken from MikeOS tutorial but the rest I have writen myself. I compile this with NASM and run it in QEMU.
So the actual question: I've declared this variable curInpLn on line 6. What ever the user types is saved on that variable and then after enter is hit it is displayed to the user with some additional messages. What I'd like to do is to clear the contents of curInpLn each time the getline function is called but for some reason I can't manage to do that. I'm quite the beginner with Assmebly at the moment.
You can compile the code to bin format and then create a floppy image of it with: "dd status=noxfer conv=notrunc if=FILENAME.bin of=FILENAME.flp" and run it in qemu with: "qemu -fda FILENAME.flp"
BITS 16
jmp start
welcomeSTR: db 'Welcome!',0
promptSTR: db 'Please prompt something: ',0
responseSTR: db 'You prompted: ',0
curInpLn: times 80 db 0 ;this is a variable to hold the input 'command'
curCharCnt: dw 0
curLnNum: dw 1
start:
mov ax, 07C0h ; Set up 4K stack space after this bootloader
add ax, 288 ; (4096 + 512) / 16 bytes per paragraph
mov ss, ax
mov sp, 4096
mov ax, 07C0h ; Set data segment to where we're loaded
mov ds, ax
call clear_screen
lea bx, [welcomeSTR] ; Put string position into SI
call print_string
call new_line
.waitCMD:
lea bx, [promptSTR]
call print_string
call getLine ; Call our string-printing routine
jmp .waitCMD
getLine:
cld
mov cx, 80 ;number of loops for loopne
mov di, 0 ;offset to bx
lea bx, [curInpLn] ;the address of our string
.gtlLoop:
mov ah, 00h ;This is an bios interrupt to
int 16h ;wait for a keypress and save it to al
cmp al, 08h ;see if backspace was pressed
je .gtlRemChar ;if so, jump
mov [bx+di], al ;effective address of our curInpLn string
inc di ;is saved in bx, di is an offset where we will
;insert our char in al
cmp al, 0Dh ;see if character typed is car-return (enter)
je .gtlDone ;if so, jump
mov ah, 0Eh ;bios interrupt to show the char in al
int 10h
.gtlCont:
loopne .gtlLoop ;loopne loops until cx is zero
jmp .gtlDone
.gtlRemChar:
;mov [bx][di-1], 0 ;this needs to be solved. NASM gives error on this.
dec di
jmp .gtlCont
.gtlDone:
call new_line
lea bx, [responseSTR]
call print_string
mov [curCharCnt], di ;save the amount of chars entered to a var
lea bx, [curInpLn]
call print_string
call new_line
ret
print_string: ; Routine: output string in SI to screen
mov si, bx
mov ah, 0Eh ; int 10h 'print char' function
.repeat:
lodsb ; Get character from string
cmp al, 0
je .done ; If char is zero, end of string
int 10h ; Otherwise, print it
jmp .repeat
.done:
ret
new_line:
mov ax, [curLnNum]
inc ax
mov [curLnNum], ax
mov ah, 02h
mov dl, 0
mov dh, [curLnNum]
int 10h
ret
clear_screen:
push ax
mov ax, 3
int 10h
pop ax
ret
times 510-($-$$) db 0 ; Pad remainder of boot sector with 0s
dw 0xAA55 ; The standard PC boot signature
I haven't written code in Assembly for 20 years (!), but it looks like you need to use the 'stosw' instruction (or 'stosb'). STOSB loads the value held in AL to the byte pointed to by ES:DI, whereas STOSSW loads the value held in AX to the word pointed to by ES:DI. The instruction automatically advances the pointer. As your variable curInpLn is 80 bytes long, you can clear it with 40 iterations of STOSW. Something like
xor ax, ax ; ax = 0
mov es, ds ; point es to our data segment
mov di, offset curInpLn ; point di at the variable
mov cx, 40 ; how many repetitions
rep stosw ; zap the variable
This method is probably the quickest method of clearing the variable as it doesn't require the CPU to retrieve any instructions from the pre-fetch queue. In fact, it allows the pre-fetch queue to fill up, thus allowing any following instructions to execute as quickly as possible.

Resources