Comparing AX register against zero - x86-16

I have a assembly program to write. I need to check the AX register, if the AX register is greater than 0 move +1 in BX, if the AX register has a value less than 0 then move -1 in BX else if AX =0 then move 0 in BX. I have the following code that does it but I am looking for an alternate solution. Please help out. Thanks
CMP AX, 0
JG GREATER
JL LESS
MOV BX, 0
GREATER:
MOV BX, 1
LESS:
MOV BX, -1

The code you gave always returns -1. Try this:
CMP AX, 0
JG GREATER
JL LESS
MOV BX, 0
JMP END
GREATER:
MOV BX, 1
JMP END
LESS:
MOV BX, -1
END:

Try this, which only requires a single conditional branch and no unconditional jumps:
mov bx, ax // copy ax to bx
sarw bx, 15 // arithmetic shift - any -ve => -1, 0 or +ve => 0
cmp ax, 0 // compare original number to zero
jle end // if it's <=, we're done
mov bx, 1 // else bx = 1
end:
NB - my x86 code is very very rusty. Also, that version of sar wasn't in the 8086, but was in the 286 and later, and didn't get particularly speedy until the 80386.
EDIT I think I found a better version for 386+ without any branches:
mov bx, ax // copy ax to bx
sarw bx, 15 // arithmetic shift - any -ve => -1, 0 or +ve => 0
cmp ax, 0 // compare original to zero
setg bl // if it was greater, bl = 1 (bh already == 0 from above)

Related

Segfault when trying to get a byte from memory address

Im trying to make Sieve of Eratosthenes work with big numbers
The problem I have is that it is giving me a segfault but idk why
It works up until about 100k
But the algorithm works if I replace cmp byte [rbx], 0 with cmp dword [rbx], 0
Im very confused as to why that is happening since all the values in the array are 0's and 1's so a byte should be enough
btw with cmp dword [rbx], 0 the results are incorrect so I cant use that
X86 64
%macro crossOut 4
xor rdi, rdi ;edi keeps track of how many numbers were crossed out
;if 0 end loop
mov rbx, %1 ;array
add rbx, %2 ;move position to starting index
mov rax, %3 ;every nth number to be crossed out
mov rbp, %4 ; array length
mov rcx, 0 ;counter
%%loop:
add rcx, rax
cmp rcx, rbp
jge %%exit
add rbx, rax
cmp byte [rbx], 0
je %%crossout
jmp %%loop
%%crossout:
mov byte [rbx], 1
inc rdi
jmp %%loop
%%exit:
cmp rdi, 0
%endmacro
See this CodeReview question for the OP's full program.
You are reading past the end of the numbers buffer because your code does not take into account the 2nd macro parameter (an offset into the array)!
The first time the offset in the array is 0, and the code will run ok. But later you add to the offset while keeping the array length the same, and so memory that does not belong to the array is addressed.
<---- RBP=10 ----->
0,0,0,0,0,0,0,0,0,0
First time RCX allows 4 iterations {2,4,6,8} less than 10:
<---- RBP=10 ----->
RBX
v
0,0,1,0,1,0,1,0,1,0
^ ^ ^ ^
1° 2° 3° 4°
Second time RCX allows 3 iterations {3,6,9} less than 10:
<---- RBP=10 ----->
RBX
v
0,0,1,0,1,0,1,1,1,0,?
^ ^ ^
1° 2° 3°
The 3° is past the buffer and, depending on total array length, at some point this buffer overrun will segfault!
The quick fix is to initialize RCX at the value for the starting index %2 instead of zeroing it.
%macro crossOut 4
xor edi, edi ;edi keeps track of how many numbers were crossed out
;if 0 end loop
mov rbx, %1 ;array
add rbx, %2 ;move position to starting index
mov rax, %3 ;every nth number to be crossed out
mov rbp, %4 ; array length
mov rcx, %2 ;counter
%%loop:
add rcx, rax
cmp rcx, rbp
jge %%exit
add rbx, rax
cmp byte [rbx], 0
je %%crossout
jmp %%loop
%%crossout:
mov byte [rbx], 1
inc rdi
jmp %%loop
%%exit:
cmp rdi, 0
%endmacro
A better fix is to establish an absolute last address that you have RBX compare against:
%macro crossOut 4
xor edi, edi ; EDI keeps track of how many numbers were crossed out
; if 0 end loop
mov rbx, %1 ; array
mov rcx, %4 ; array length
add rcx, rbx ; Last address
add rbx, %2 ; move position to starting index
mov rax, %3 ; every nth number to be crossed out
%%loop:
add rbx, rax
cmp rbx, rcx
jae %%exit
cmp byte [rbx], 0
jne %%loop
%%crossout:
mov byte [rbx], 1
inc rdi
jmp %%loop
%%exit:
cmp rdi, 0
%endmacro
Please notice that
je %%crossout
jmp %%loop
%%crossout:
is better written as
jne %%loop
%%crossout:

find all paths from top left corner to right bottom corner assembly

I am new to assembly programming, currently taking online course.
Original problem was to count number of paths from top left corner to bottom right corner. But I found a good solution to that here:
https://www.geeksforgeeks.org/count-possible-paths-top-left-bottom-right-nxm-matrix/
Based on the combinatorics solution I should be able to find all paths in a binary manner.
First question, do you know a faster way to count paths?
Searched for the solution to print all paths in:
https://www.geeksforgeeks.org/print-all-possible-paths-from-top-left-to-bottom-right-of-a-mxn-matrix/
But did not notice any using the binary approach with seemed adequate for assembly.
Searching a bit more online I found:
https://www.baeldung.com/cs/generate-k-combinations
Revolving door algorithm was well detailed, and I calculate it to be O (number of combinations) * O (width or height of matrix (for printing) -1) * O (branching loops) on time complexity and O (width or height + 1) on space. Second question is this a correct assumption? If not, what is the correct complexity? Is it faster than the other solutions posted for finding all paths to this problem? Those are stated to be O(2^(width*height))
Third question: Who wrote this algorithm? Where can I find more like it?
And lastly, I will post my newbie 32-bit assembly pasta code for fasm, should work on matrixes larger than 3 x 3 smaller than 32 x 32(not recommended to go above 16 x 16 that is already a lot of combinations and only omitting the print instructions), any improvements are more than welcome. Thank you.
format PE console
entry start
include 'win32a.inc'
; ===============================================
struct MAT
h db ? ; X coordinate.
w db ? ; Y coordinate.
ends
; ===============================================
section '.bss' readable writeable
; Declare the uninitialized table in memory:
path_matrix MAT ?
count dd ?
indices db path_matrix.w - 1 dup ?
end_indices:
; ===============================================
section '.text' code readable executable
start:
call read_hex
mov [path_matrix.h], al ; down will be 0
call read_hex
mov [path_matrix.w], al ; right will be 1
dec eax
mov ecx, eax
initialize:
mov ebx, ecx
dec ebx
mov byte[indices+ecx], bl
loop initialize
movzx ebx, [path_matrix.h]
dec ebx
add ebx, eax
mov byte[indices+eax+1], bl
mov eax, ebx
print_combination:
inc [count]
movzx ebx, [end_indices - indices]
dec ebx
xor eax, eax
print_loop:
xor esi, esi
inc esi
mov cl, byte[indices + ebx ]
shl esi, cl
xor eax, esi
dec ebx
cmp ebx, 0
jnz print_loop
call print_eax_binary
branch:
lea edi, [indices +1]
movzx eax, [path_matrix.w] ; check if withd is eaven, if true matrix is odd (w -1)
shr eax, 1
jnc odd
eaven:
movzx eax, byte[edi]
cmp eax, 0
jle eaven_indice
dec eax
mov byte[edi], al
jmp print_combination
eaven_indice:
inc edi
try_to_increase:
movzx ebx, byte[edi]
inc ebx
cmp bl, [edi+1]
jl increase
lea ecx, [edi-indices+1]
cmp cl, [path_matrix.w]
jl increase_indice
jmp fin
increase:
mov byte[edi], bl
dec ebx
mov byte[edi-1], bl
jmp print_combination
odd:
movzx eax, byte[edi]
inc eax
cmp al, [edi+1]
jge increase_indice
mov byte[edi], al
jmp print_combination
increase_indice:
inc edi
try_decrease:
lea eax, [edi - indices]
cmp byte[edi], al
jl eaven_indice
decrease:
movzx ebx, byte[edi-1]
mov byte[edi], bl
sub eax, 2
mov byte[edi-1], al
jmp print_combination
fin:
mov eax, [count]
call print_eax
; Exit the process:
push 0
call [ExitProcess]
include 'training.inc'
The solution is not binary because paths from the top or left can overlap - creating duplicates. Working backward the solution is additive - sum of paths from left and paths from top.
Which leads to a simple solution:
_m = 16
_n = 16
ddp rq _m
; initialize first position
mov [ddp + (_n-1)*8], 1
mov ecx, _m
outer:
push rcx
mov ecx, _n
xor eax, eax
inner:
add rax, [ddp + (rcx-1)*8]
mov [ddp + (rcx-1)*8], rax
loop inner
pop rcx
loop outer
There is a closed form solution, but it's a little tricky to reduce the expression in such a way as to cover a large number of inputs:
; number of paths from one corner to opposite diagonal corner of M x N matrix
; RCX : N, RDX : M
numberOfPaths:
mov eax,1
mov r9,1
lea r8,[rcx+rdx-1]
jmp .try
.more:
mul rcx
inc ecx
div r9
inc r9
.try:
cmp rcx,r8
jc .more
retn
It can be reduced further with more code.

Assembly safes and keys- why it won't work?

So we have like this safes challenge in assembly, you need to create safes and keys that will break them and end the infinite loop.
Here's an example for a safe:
loopy:
mov ax, [1900]
cmp ax,1234
jne loopy
and a key:
loopy2:
mov ax, 1234
mov [1900],ax
jmp loopy2
So I have a safe and a key, and I don't understand why it doesn't work:
here's my safe:
org 100h
mySafe:
mov dx,5
mov ax, [5768h]
mov bx,7
mov word [180h],2
mul word [180h]
mov [180h],bx
push ax
dec bx
mov cx,dx
mov ax,dx
loopy1:
add bx,ax
loop loopy1
dec bx
pop ax
add ax,bx
mul word [180h]
cmp ax,350
jne mySafe
And here's my key:
org 100h
loopy:
mov word [5768h],10
jmp loopy
ret
The right answer to break the loop should be 10 and it works when I put in on the safe, somehow with the key it doesn't work and I can't figure out why..
(the "word" is needed for nasm)
The value in dx used as the counter for the loop instruction comes from the first mul instruction.
This multiplication is just doubling the key, so dx is either 0 or 1 (an easy way to see this is to think of the multiplication as a left shift by one or by remembering that the sum of two n-bit numbers has at most n+1 bits)
If dx is zero, the whole loopy1 block does nothing (as dx also sets ax) and the value in ax at the end of the safe is 7*(5 +2k) where k is the key (see the commented code below).
It is then easy to see that 350 = 7*(5+2k) => 2k = 45 has no solution. Therefore no key for which dx is zero can unlock the safe.
A key has dx 0 iif its value is less than 32768 (again, this is easy to see when thinking of the multiplication as a left shift by one).
Corollary: 10 cannot be a solution.
safe:
mov dx,5
mov ax, [k] ;ax = k (key)
mov bx,7
mov word [aux],2
mul word [aux] ;dx = 0 ax = 2k
mov [aux],bx ;aux = 7
push ax ;ax = 2k
dec bx ;bx = 6
dec bx ;bx = 5
pop ax ;ax = 2k
add ax,bx ;ax = 5 + 2k
mul word [aux] ;ax = 7*(5 +2k)
cmp ax,350
ret
If there is a key that unlocks the safe then it must be greater or equal to 32768 so that dx is 1 after the first multiplication.
With this condition, the value in ax at the end of the safe can be written as 7*(6 + (2k & 0xffff)) => k & 0x7fff = 22.
Adding the condition stated at the very beginning of this section, the final value for k is 32768 + 22 = 32790 or 0x8016 in hex.
I've leaped quite a few logical steps in manipulating the equation and forming the result but, again, thinking of 2k as a shift may help visualize them.
Corollary: Due to the algebraic structure involved, this is the only solution.
safe:
mov dx,5
mov ax, [k] ;ax = k
mov bx,7
mov word [aux],2
mul word [aux] ;dx:ax = 2k
mov [aux],bx ;[aux] = 7
push ax ;dx = 1 ax = 2k & 0xffff
dec bx ;bx = 6
mov cx,dx ;cx = 1
mov ax,dx ;ax = 1
loopy1:
add bx,ax ;bx = 6 + 1
dec cx
jnz loopy1
dec bx ;bx = 6
pop ax ;ax = 2k & 0xffff
add ax,bx ;ax = 6 + (2k & 0xffff)
mul word [aux] ;ax = 7*(6 + (2k & 0xffff))
cmp ax,350
ret
Considering that you have a mov dx, 5 before the first multiplication, did you (or the author of the safe) forget that mul affects dx?
If you wrap the first mul in push dx / pop dx (or just move mov dx, 5 after it), you would get, at the end of the safe, a value in ax equals to 7*(30 +2k) which implies k = 10 indeed.

Sorting a list of ten numbers with selection sort in assembly language

sorting a list of ten numbers with selection sort in assembly language.
How does i convert this bubble sort method into selection sort method
`[org 0x0100]
jmp start
data: dw 60, 55, 45, 50, 40, 35, 25, 30, 10, 0
swap: db 0
start: mov bx, 0 ; initialize array index to zero
mov byte [swap], 0 ; rest swap flag to no swaps
loop1: mov ax, [data+bx] ; load number in ax
cmp ax, [data+bx+2] ; compare with next number
jbe noswap ; no swap if already in order
mov dx, [data+bx+2] ; load second element in dx
mov [data+bx+2], ax ; store first number in second
mov [data+bx], dx ; store second number in first
mov byte [swap], 1 ; flag that a swap has been done
noswap: add bx, 2 ; advance bx to next index
cmp bx, 18 ; are we at last index
jne loop1 ; if not compare next two
cmp byte [swap], 1 ; check if a swap has been done
je start ; if yes make another pass
mov ax, 0x4c00 ; terminate program
int 0x21`
The key here is to change your loop. Currently it's swapping adjacent numbers. You need to change it to copy the rightmost element into a register, and shift the pre-existing sorted array to the right until the element you just shifted is greater than or equal to the previously rightmost element you just copied into your register.
Maybe this will be helpfull. I wrote this a long time ago. Realmode intel assembler.
MAIN.ASM
SSTACK SEGMENT PARA STACK 'STACK'
DW 128 DUP(?)
SSTACK ENDS
DSEG SEGMENT PUBLIC 'DATA'
S LABEL BYTE
ARR DB 'IHGFED27182392JASKD1O12312345CBA'
LEN EQU ($-S)
PUBLIC TMP
PUBLIC MIN
TMP DW ?
MIN DW ?
DSEG ENDS
CSEG SEGMENT 'CODE'
ASSUME CS:CSEG, SS:SSTACK, DS:DSEG
EXTRN OUTPUT:NEAR
EXTRN SORT:NEAR
START: MOV AX, DSEG
MOV DS, AX
MOV BX, OFFSET ARR
MOV CX, LEN
CALL OUTPUT
MOV AX, 60
CMP AX, 0
JZ NO_SORT
CMP AX, 1
JZ NO_SORT
MOV BX, OFFSET ARR
MOV CX, LEN
CALL SORT
NO_SORT: MOV BX, OFFSET ARR
MOV CX, LEN
CALL OUTPUT
MOV AH, 4CH
MOV AL, 0
INT 21H
CSEG ENDS
END START
SORT.ASM
DSEG SEGMENT PUBLIC 'DATA'
EXTRN TMP:WORD
EXTRN MIN:WORD
DSEG ENDS
CSEG SEGMENT 'CODE'
ASSUME CS:CSEG, DS:DSEG
PUBLIC SORT
SORT PROC; (AX - N, BX - ARRAY ADDRESS, CX - ARRAY LENGTH)
PUSH SI
PUSH DI
PUSH DX
CALL COMPARE_MIN
DEC CX
MOV AX, CX
XOR SI, SI
XOR DI, DI
L1:
PUSH CX
MOV MIN, SI
MOV TMP, DI
INC DI
MOV CX, AX
L2:
MOV DH, BYTE PTR[BX+DI]
PUSH SI
MOV SI, MIN
MOV DL, BYTE PTR[BX+SI]
POP SI
CMP DH, DL
JA OLD_MIN
NEW_MIN: MOV MIN, DI
OLD_MIN: INC DI
DEC CX
CMP CX, TMP
JNZ L2
SWAP: PUSH DI
MOV DI, MIN
MOV DL, BYTE PTR[BX+DI]
MOV DH, BYTE PTR[BX+SI]
MOV BYTE PTR [BX+SI], DL
MOV BYTE PTR [BX+DI], DH
POP DI
INC SI
MOV DI, SI
POP CX
LOOP L1
POP DX
POP DI
POP DI
RET
SORT ENDP
COMPARE_MIN PROC; (AX - A, CX - B CX - MIN)
PUSH AX
CMP AX, CX
JB B__A
JA A__B
A__B: MOV CX, CX
JMP EX
B__A: MOV CX, AX
JMP EX
EX: POP AX
RET
COMPARE_MIN ENDP
CSEG ENDS
END
OUTPUT.ASM
CSEG SEGMENT 'CODE'
ASSUME CS:CSEG
PUBLIC OUTPUT
OUTPUT PROC ; (BX - ARRAY ADDRESS, CX - ARRAY LENGTH)
PUSH DX
PUSH SI
PUSH AX
XOR SI, SI
MOV AH, 02H
OUTARR: MOV DL,[BX+SI]
INT 21H
INC SI
LOOP OUTARR
MOV DL, 10
INT 21H
POP AX
POP SI
POP DX
RET
OUTPUT ENDP
CSEG ENDS
END

Jumping to random code when using IDIV

I am relatively new to assembler, but when creating code what works with arrays and calculates the average of each row, I encountered a problem that suggests I don't know how division really works. This is my code:
.model tiny
.code
.startup
Org 100h
Jmp Short Start
N Equ 2 ;columns
M Equ 3 ;rows
Matrix DW 2, 2, 3 ; elements
DW 4, 6, 6 ; elements]
Vector DW M Dup (?)
S Equ Type Matrix
Start:
Mov Cx, M;20
Lea Di, Vector
Xor Si, Si
Cols: Push Cx
Mov Cx, N
Xor Bx, Bx
Xor Ax, Ax
Rows:
Add Ax, Matrix[Bx][Si]
Next:
Add Bx, S*M
Loop Rows
Add Si, S
Mov [Di], Ax
Add Di, S
Pop Cx
Loop Cols
Xor Bx, Bx
Mov Cx, M
Mov DX, 2
Print: Mov Ax, Vector[Bx]
IDiv Dx; div/idiv error here
Add Bx, S
Loop Print
.exit 0
There are no errors when compiling. Elements are counted correctly, but when division happens the debugger shows the program jumping to apparently random code. Why is this happening and how can I resolve it?
If you use x86 architecture, IDiv with 16-bit operand will also take Dx as a part of the integer to be divided and throw an exception (interrupt) if the quotient is too large to fit in 16bits.
Try something like this:
Mov Di, 2
Print: Mov Ax, Vector[Bx]
Cwd ; sign extend Ax to Dx:Ax
IDiv Di

Resources