I am a begginer in Assembly language (TASM 86x) working on my first program assignment. It's not complicated in nature, however being new to this language I'm having a hard time figuring out a simple bubble sort.
So far I have only programed witch C++, hardest part overall is to grasp the syntax.
Task is to take any string (typed in by user) and rearrange it ascending by ASCII value (as in, if you type beda it should give abde)
I'm not certain about my output, but that should come after the sort is done I'm confused, because it just allows me to input my string and then quits to the command prompt. Can't trace where I've made a mistake where it points to the end of the code prematurely.
I would be very grateful if someone more experienced would take a look at my code and point me in the right direction and maybe even explain a thing or two to a newbie
.model small
.stack 100h
.data
request db 'Enter symbols:', 0Dh, 0Ah, '$'
buffer db 100, ?, 100 dup (0)
.code
start:
MOV ax, @data
MOV ds, ax
; request
MOV ah, 09h
MOV dx, offset request
int 21h
; read string ;reading string to buffer
MOV dx, offset buffer
MOV ah, 0Ah
INT 21h
MOV si, offset buffer
INC si ;going from buffer size to actual length
;of the string
MOV cl, [si] ;string length - loop counter
mov ch, [si] ;string length - loop counter
mov bl, [si] ;bl will be used to reset inner loop counter
DEC cl ;correcting the values, since count goes
dec ch ; from 0 to n-1 instead of 1 to n
inc si ;moving to strings first byte
outer: ;outer loop
dec ch ;decrease counter each pass
jz ending ;when counter reaches 0 end program
mov cl, bl ; reset inner loop counter value
inner: ;inner loop
mov al,byte ptr[si] ;assigning byte(sybol) to al
mov ah, byte ptr[si+1] ;assigning following byte(symbol) to ah
cmp al,ah ;compare the two
jle after_switch ;if the latter's value is higher, no need to switch
problems with the switch, not sure if it will work right in assembly
mov bh, al ;main problem-switching values, tried a few different
mov al, ah ;ways of doing it (will show them below), but to no avail
mov ah, bh ;using familiar C syntax
jmp output ;outputing the value
after_switch: ;no switch needed
somewhere in the outer switch there is supposed to be jump to output, however i cant figure out the way to include it without messing up the rest of the sequence
inc [si] ;going to the next byte
dec cl ;decreasing inner loop counter
jnz inner ;back to the beginning of inner loop until counter reaches 0 (resetting in the outer loop)
jmp outer ;if counter reaches zero, get back to outer
output: ;outputting value from the very first bit
mov ah, 2
mov dl, al ;which after switch is supposed to be stored in al
int 21h
jmp inner ;returning to inner loop to run next course of comparison
ending:
MOV ax, 4c00h
INT 21h
end start
Previously tried methods of switch in inner loop
mov al,[si+1]
mov byte ptr[si+1],[si]
mov byte ptr[si], al
returns illegal memory reference error, but this question has already been answered on this board in the past, found it.
tried the same method, but utilizing the dx:di register
mov al, byte ptr[si+1]
mov dx:[di], [si]
mov byte ptr[si+1], dx:[di]
mov byte ptr[si], al
returns illegal override register error, couldn't find anything on it
Logical error
mov al, byte ptr[si+1]
mov dx:[di], [si] <<-- there is no dx:[di] register.
mov byte ptr[si+1], dx:[di] <<-- memory to memory move not allowed.
mov byte ptr[si], al <<-- `byte ptr` is superflous, because `al` is already byte sized.
You can use a segment register here, but because you're only working with the ds
segment there's no need for that.
So this would be valid:
mov DS:[di],si <-- ds segment (but DS is already the default)
Note that a memory to memory move is not allowed, data has to either come from a constant:
mov [di],1000 <-- direct assignment using a constant
Or has to pass through a register
mov ax,[di]
mov [si],ax <--- memory to memory must be a 2 step process.
Remember [reg]
is memory; reg
is a register
Another error is here:
inc [si] <<-- error, increases some memory location, not `si` the pointer.
dec cl ;decreasing inner loop counter
jnz inner ;back to the beginning of inner loop until counter reaches 0 (resetting in the outer loop)
jmp outer ;if counter reaches zero, get back to outer
This should be:
inc si ;next char in the string
dec cl ;decreasing inner loop counter
jnz inner ;back to the beginning of inner loop until counter reaches 0 (resetting in the outer loop)
jmp outer ;if counter reaches zero, get back to outer
Simplification
Flipping two values around does not require mov
s. Replace this code:
mov bh, al ;main problem-switching values, tried a few different
mov al, ah ;ways of doing it (will show them below), but to no avail
mov ah, bh ;using familiar C syntax
With this much simpler variant, saving a register in the process:
xchg ah, al ;flip chars around
After you're done flipping al
and ah
around you need to write them back to memory, otherwise all that work is for nothing.
xchg ah, al ;flip chars around
mov [si],al ;save flipped values
mov [si+1],ah
Reading past the end of the string
In this snippet you've got the correct idea, but because you're letting [si]
run all the way to the end of the string you're reading 1 byte too many.
inner: ;inner loop
mov al,byte ptr[si] ;<<-- both are correct, but [si+1] will read
mov ah, byte ptr[si+1] ;<<-- past the end of the string at the last byte
So you need to change this section:
DEC cl ;correcting the values, since count goes
dec ch ; from 0 to n-1 instead of 1 to n
To this:
sub bl,2 ;if bl is used to reset inner loop counter it must be
;adjusted as well.
;sub cl,2 ;inner loop from 0 to n-2
mov cl,bl ;bl=cl, so a mov makes more sense
dec ch ;outer loop from 0 to n-1 instead of 1 to n
Finally efficiency tips
Never read from memory if you don't have to.
Change this code:
MOV si, offset buffer
INC si ;going from buffer size to actual length
;of the string
MOV cl, [si] ;string length - loop counter
mov ch, [si] ;string length - loop counter
mov bl, [si] ;bl will be used to reset inner loop counter
To this:
MOV si, offset buffer+1 ;start at length byte of the string
MOV cl, [si] ;string length - loop counter
mov ch, cl ;string length - loop counter
mov bl, cl ;bl will be used to reset inner loop counter
There are more speed optimizations that can be done but I don't want to overcomplicate things.