I'm new to assembly and I am trying to swap the contents between two arrays. I have this code so far and after testing it, I've verified it works. However, I am wondering if this is the most efficient way to get the desired result or if there is another possible solution to this that is more efficient?
arrW WORD 100h, 200h, 300h
arrSW SWORD -140, 200, -300
mov ax, arrW
xchg ax, arrSW
xchg ax, arrW
mov ax, [arrW +2]
xchg ax, [arrSW +2]
xchg ax, [arrW +2]
mov ax, [arrW + 4]
xchg ax, [arrSW +4]
xchg ax, [arrW +4]
mov ax, arrW
xchg ax, arrSW
xchg ax, arrW
mov ax, [arrW +2]
The first thing that struck me is that second xchg. There's no sense in loading the AX register right before the other load of AX in the following instruction. The first rewrite that also gives a 20% speed increase on 8086 therefore is:
mov ax, [arrW]
xchg ax, [arrSW]
mov [arrW], ax
mov ax, [arrW + 2]
xchg ax, [arrSW + 2]
mov [arrW + 2], ax
mov ax, [arrW + 4]
xchg ax, [arrSW + 4]
mov [arrW + 4], ax
A solution where you avoid using the xchg instruction doesn't pay on 8086, but is the way to go on x86 in general. eg. Next snippet is 10% slower on 8086:
mov ax, [arrW]
mov bx, [arrSW]
mov [arrW], bx
mov [arrSW], ax
A loop can't beat your current unrolled code, but if the arrays should become larger then next is what it could look like:
mov cx, 3
mov si, OFFSET arrW
mov di, OFFSET arrSW
More:
mov ax, [si]
mov dx, [di]
mov [si], dx
mov [di], ax
add si, 2
add di, 2
dec cx
jnz More
In case the arrays arrW and arrSW follow each other in memory, the same loop is better written as:
mov bx, OFFSET arrW
More:
mov ax, [bx]
mov dx, [bx + 6]
mov [bx], dx
mov [bx + 6], ax
add bx, 2
cmp bx, OFFSET arrSW
jb More
If the CPU supports 32-bit registers, then working with these dwords can halve the number of iterations that are required. If the count of elements is odd, we peel off one word sized swap:
mov cx, 39
mov si, OFFSET arrW
mov di, OFFSET arrSW
shr cx, 1
jnc More ; Count was even
mov ax, [si]
mov dx, [di]
mov [si], dx
mov [di], ax
add si, 2
add di, 2
More:
mov eax, [si]
mov edx, [di]
mov [si], edx
mov [di], eax
add si, 4
add di, 4
dec cx
jnz More
The above code peels off one word sized swap at the start of the loop. As #PeterCordes wrote in his comment below this answer, it is generally beter to put the peeled-off swap at the end (for reasons of data alignment). Next is that version:
mov cx, 39
mov si, OFFSET arrW
mov di, OFFSET arrSW
shr cx, 1 ; -> CF is set if count is odd
jz Next \
More: |
mov eax, [si] |
mov edx, [di] |
mov [si], edx |
mov [di], eax | Nothing changes the CF
lea si, [si + 4] |
lea di, [di + 4] |
dec cx |
jnz More |
Next: /
jnc Done ; (*) Count was even
mov ax, [si]
mov dx, [di]
mov [si], dx
mov [di], ax
Done:
Related
I am trying to make a 8086 program in which I input an array of string and then the program prints them. But the program prints only the 1st characters of input strings and jumbled text.
This is the code where I attempted to do it.
data segment
; Definicija podataka
poruka1 DB "Input array length: $"
strN DB " "
N DW 0
poruka2 DB "Input string: $"
strM DB " "
niz1 DB 16 dup(?)
ends
; Deficija stek segmenta
stek segment stack
dw 128 dup(0)
ends
; Ucitavanje znaka bez prikaza i cuvanja
keypress macro
push ax
mov ah, 08
int 21h
pop ax
endm
; Isis stringa na ekran
writeString macro s
push ax
push dx
mov dx, offset s
mov ah, 09
int 21h
pop dx
pop ax
endm
; Kraj programa
krajPrograma macro
mov ax, 4c02h
int 21h
endm
code segment
; Novi red
novired proc
push ax
push bx
push cx
push dx
mov ah,03
mov bh,0
int 10h
inc dh
mov dl,0
mov ah,02
int 10h
pop dx
pop cx
pop bx
pop ax
ret
novired endp
; Ucitavanje stringa sa tastature
; Adresa stringa je parametar na steku
readString proc
push ax
push bx
push cx
push dx
push si
mov bp, sp
mov dx, [bp+12]
mov bx, dx
mov ax, [bp+14]
mov byte [bx] ,al
mov ah, 0Ah
int 21h
mov si, dx
mov cl, [si+1]
mov ch, 0
kopiraj:
mov al, [si+2]
mov [si], al
inc si
loop kopiraj
mov [si], '$'
pop si
pop dx
pop cx
pop bx
pop ax
ret 4
readString endp
; Konvertuje string u broj
strtoint proc
push ax
push bx
push cx
push dx
push si
mov bp, sp
mov bx, [bp+14]
mov ax, 0
mov cx, 0
mov si, 10
petlja1:
mov cl, [bx]
cmp cl, '$'
je kraj1
mul si
sub cx, 48
add ax, cx
inc bx
jmp petlja1
kraj1:
mov bx, [bp+12]
mov [bx], ax
pop si
pop dx
pop cx
pop bx
pop ax
ret 4
strtoint endp
; Konvertuje broj u string
inttostr proc
push ax
push bx
push cx
push dx
push si
mov bp, sp
mov ax, [bp+14]
mov dl, '$'
push dx
mov si, 10
petlja2:
mov dx, 0
div si
add dx, 48
push dx
cmp ax, 0
jne petlja2
mov bx, [bp+12]
petlja2a:
pop dx
mov [bx], dl
inc bx
cmp dl, '$'
jne petlja2a
pop si
pop dx
pop cx
pop bx
pop ax
ret 4
inttostr endp
start:
; postavljanje segmentnih registara
ASSUME cs: code, ss:stek
mov ax, data
mov ds, ax
; Mesto za kod studenata
writeString poruka1
; Unos broja elemenata
push 3
push offset strN
call readString
; Pretvaranje strN u broj
push offset strN
push offset N
call strtoint
call novired
writeString strN
; Unos elemenata niza
mov cx, N
mov di, 0
mov si, 0
unos:
call novired
writeString poruka2
push 6
push offset strM
call readString
mov al, strM
mov niz1[di], al
add di, 2
add si, 1
loop unos
call novired
mov cx, N
mov di, 0
mov si, 0
ispis:
writeString niz1[di]
add di, 2
add si, 1
loop ispis
krajPrograma
ends
end start
I input an array of string and then the program prints them. But the program prints only the 1st characters of input strings ...
What else could the program print if the 1st character is all that you store in memory?
mov al, strM
mov niz1[di], al
add di, 2
... and jumbled text.
With add di, 2 you leave an undetermined byte between any two of these 1st characters. That's not going to look nice. Make that byte the DOS string terminator $.
Your writeString macro does not load an address but a word from memory. That's not going to work. The invokation writeString niz1[di] gets expanded into mov dx, offset niz1[di]. What you need here is LEA instead of MOV.
Try the following:
....
unos:
call novired
writeString poruka2
push 6
push offset strM
call readString
mov al, strM
MOV AH, "$"
mov niz1[di], AX
add di, 2
loop unos
call novired
mov cx, N
mov di, 0
ispis:
LEA DX, niz1[di]
MOV AH, 09h
INT 21h
add di, 2
loop ispis
...
I am currently writing an OS from complete scratch(making my own bootloader etc), and I am attempting to adapt VESA mode.
I have read documentation, it all makes sense..all but just a few things.
This is directly from the documentation(I have it implemented differently):
vbe_set_mode:
mov [.width], ax
mov [.height], bx
mov [.bpp], cl
sti
push es ; some VESA BIOSes destroy ES, or so I read
mov ax, 0x4F00 ; get VBE BIOS info
mov di, vbe_info_block
int 0x10
pop es
cmp ax, 0x4F ; BIOS doesn't support VBE?
jne .error
mov ax, word[vbe_info_block.video_modes]
mov [.offset], ax
mov ax, word[vbe_info_block.video_modes+2]
mov [.segment], ax
mov ax, [.segment]
mov fs, ax
mov si, [.offset]
.find_mode:
mov dx, [fs:si]
add si, 2
mov [.offset], si
mov [.mode], dx
mov ax, 0
mov fs, ax
cmp [.mode], 0xFFFF ; end of list?
je .error
push es
mov ax, 0x4F01 ; get VBE mode info
mov cx, [.mode]
mov di, mode_info_block
int 0x10
pop es
cmp ax, 0x4F
jne .error
mov ax, [.width]
cmp ax, [mode_info_block.width]
jne .next_mode
mov ax, [.height]
cmp ax, [mode_info_block.height]
jne .next_mode
mov al, [.bpp]
cmp al, [mode_info_block.bpp]
jne .next_mode
; If we make it here, we've found the correct mode!
mov ax, [.width]
mov word[vbe_screen.width], ax
mov ax, [.height]
mov word[vbe_screen.height], ax
mov eax, [mode_info_block.framebuffer]
mov dword[vbe_screen.physical_buffer], eax
mov ax, [mode_info_block.pitch]
mov word[vbe_screen.bytes_per_line], ax
mov eax, 0
mov al, [.bpp]
mov byte[vbe_screen.bpp], al
shr eax, 3
mov dword[vbe_screen.bytes_per_pixel], eax
mov ax, [.width]
shr ax, 3
dec ax
mov word[vbe_screen.x_cur_max], ax
mov ax, [.height]
shr ax, 4
dec ax
mov word[vbe_screen.y_cur_max], ax
; Set the mode
push es
mov ax, 0x4F02
mov bx, [.mode]
or bx, 0x4000 ; enable LFB
mov di, 0 ; not sure if some BIOSes need this... anyway it doesn't hurt
int 0x10
pop es
cmp ax, 0x4F
jne .error
clc
ret
.next_mode:
mov ax, [.segment]
mov fs, ax
mov si, [.offset]
jmp .find_mode
.error:
stc
ret
.width dw 0
.height dw 0
.bpp db 0
.segment dw 0
.offset dw 0
.mode dw 0
What I am confused about is, why does it assign the segment to the video modes pointer plus 2?
I get that the video mode pointer has an offset:segment, but I am just confused as to why we assign video mode pointer + 2 to the segment, and why we add si by two after we assign the offset and segment to the dx register.
mov ax, word[vbe_info_block.video_modes]
mov [.offset], ax
mov ax, word[vbe_info_block.video_modes+2]
mov [.segment], ax
why does it assign the segment to the video modes pointer plus 2? I get that the video mode pointer has an offset:segment, but I am just confused as to why we assign video mode pointer + 2 to the segment
A far pointer is stored in memory with a word-sized offset followed by a word-sized segment.
The offset is stored at vbe_info_block.video_modes, and the segment is stored in the following word which has an address that is 2 more, so at [vbe_info_block.video_modes + 2]
mov dx, [fs:si]
add si, 2
mov [.offset], si
and why we add si by two after we assign the offset and segment to the dx register.
We don't assign the offset and segment to the DX register!
The far pointer we retrieved (and put in FS:SI) points to a list of word-sized mode numbers. It's a mode number that we load in the DX register. And the add si, 2 mov [.offset], si is there so the loop can iterate over all the words in the list.
I'm trying to create a proc in TASM that accepts arrays as parameters. Problem is, the result is all messed up. This is the code:
TextParameter equ [bp+8]
MenuColorParameter equ [bp+6]
RowToPrint equ [bp+4]
PrintBar proc
push bp
mov bp, sp
mov dh, RowToPrint
mov dl, 0
mov bh, 0
mov ah, 2
int 10h
mov si, 0
mov cx, 1
##Print:
mov ah, 9
mov al, [TextParameter+si]
mov bl, [MenuColorParameter+si]
int 10h
mov ah, 2
inc dl
int 10h
inc si
cmp si, MENU_LEN ; const in DATASEG
jc ##Print
mov ah, 2
mov bh, 0
mov dl, 0
mov dh, 2
int 10h
pop bp
ret 6
PrintBar endp
When referencing the array through the DATASEG (as in mov al, Array[si]), the proc works as expected.
I'm assuming that you're passing the arrays by-address (i.e. their offsets), since the arguments seem to be 2 bytes each. If so, you need to first load that address into a register, and then add si and do another read from memory:
mov bx, TextParameter ; expands to mov bx,[bp+8]
mov al, [bx+si]
mov bx, MenuColorParameter ; expands to mov bx,[bp+6]
mov bl, [bx+si]
In addition to Michael's answer, I found the problem - int 10h ah=9 and int 10h ah=2 expect bh as the video page number. After assigning al and bl, I needed to mov bh, 0. The ##Print label is now this:
##Print:
mov ah, 9
mov bx, TextParameter
mov al, [bx+si]
mov bx, MenuColorParameter
mov bl, [bx+si]
mov bh, 0
int 10h
mov ah, 2
inc dl
int 10h
inc si
cmp si, MENU_LEN ; const in DATASEG
jc ##Print
I am stuck at point where I need to read 3 numbers from created buffer of text file. I've tryed reading every byte, but no luck if the number is bigger than 9.
Steps that I do:
Open text file
Read file's content into created buffer
Put every buffers byte into different register (ax, bx, cx, dx).
The problem is, it reads 1 byte (mov ax, buffer[0]) at a time: if my text file is (10 10 1), it reads 1 then 0 then space symbol(ascii 20) and so on. Should I do cycle that converts and adds every byte to one register while it doesn't detect space symbol? Or is there a possibility to read whole number at one time? Here's the code:
.model small
bufferLen equ 16
.stack 100h
.data
duom db "duom.txt", 0
fident dw 0
buffer db bufferLen dup (?)
.code
start:
mov dx, #data
mov ds, dx
mov bx, 81h
tikrinam: ; not important
mov ax, es:[bx]
inc bx
cmp al, 13
je openf
cmp al, 20h
je tikrinam
cmp ax, "?/"
jne openf
mov ax, es:[bx]
cmp ah, 13
je abouthlp
jmp openf
abouthlp:
mov dx, offset about
mov ah, 09h
int 21h
jmp ending
openf:
mov ah, 3Dh
mov al, 0
mov dx, offset duom
int 21h
mov [fident], ax
readf:
mov ah, 3Fh
mov bx, [fident]
mov cx, bufferLen
mov dx, offset buffer
int 21h
mov al, buffer[0]
mov bl, buffer[1]
mov cl, buffer[2]
I've found the solution if anyone is having same problem:
changeNumbers:
push ax
mov ax, 0
cmp cl, 0
je change
temp1:
mov ch, 0
mov cl, buffer[si]
inc si
cmp cl, 32
je changeNumbers
cmp cl, 0
je changeNumbers
sub cl, 48
mul abc
add ax, cx
jmp temp1
Basically what I did was read every byte and if the number is >9 then add cx to ax and multiply it by 10. Then just push it to stack for further usage. Brain is an amazing thing, I'd say.
I have this code to copy the array "NUMBERS" to "DEST" such that no number will repeat (in this case it should be : 1,2,5,4,7)
The code works but now I need to print the array "DEST". How can I do that ?
data segment
NUMBERS db 1,2,1,1,1,5,5,4,7,7
DEST dt ?
data ends
code segment
assume ds:data, cs:code
start: mov ax, data
mov ds, ax
mov ax, 0a0ah
mov di, offset NUMBERS
mov bx, 0h
loop2:mov cl, [di]
mov si, offset DEST
mov ch, [si]
loop1:cmp ch, cl
je dontadd
inc si
mov ch, [si]
dec ah
jnz loop1
mov si, offset DEST
add si, bx
inc bx
mov [si], cl
dontadd:mov ah, 0ah
inc di
dec al
jnz loop2
mov ah, 4ch
int 21h
code ends
end start
You can use INT 21h, AH=02h to print a single character to STDOUT.
MOV CX,10
MOV SI,OFFSET DEST
print:
MOV DL,[si]
OR DL,DL
JE done
ADD DL,'0' ; <-- Convert numeric value in DL into ASCII code
MOV AH,02h
INT 21h
MOV DL,' ' ; Throw in a space to make things pretty
INT 21h
INC SI
LOOP print
done:
If elements in NUMBERS have values over 9 then you need more elaborate conversion of the numeric values into ASCII.
First convert numbers to string then print them.You can use this two macros for printing numbers:
printstr macro str
push ax
push dx
lea dx,str
mov ah,9
int 21h
pop dx
pop cx
endm
printnum macro n
local o,w,s,n5,lb1,lb2,lb3
pusha
push si
push di
jmp w
s db 7 dup('$')
w:
mov si,0
mov cx,7
o:mov s[si],'$'
inc si
loop o
mov si,0
xor ax,ax
mov ax,n
xor dx,dx
cmp ax,0
jge n5
mov bl,'-'
mov s[si],bl
inc si
neg ax
n5:mov cx,10
div cx
or dl,30h
mov s[si],dl
xor dx,dx
inc si
cmp ax,0
jne n5
mov si,0
cmp s[si],'-'
jne lb1
inc si
lb1:mov di,si
lb3:cmp s[si],'$'
je lb2
mov al,s[si]
push ax
inc si
jmp lb3
lb2:pop ax
mov s[di],al
inc di
cmp di,si
jl lb2
printstr s
pop di
pop si
popa
endm