Search code examples
sortingassemblyx86bubble-sorttasm

Assembly x86 TASM Sorting


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


Solution

  • 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 movs. 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.