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

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.

Related

What is this assignment asking me to do, about summing the digits of a value 0425h?

I have this assembly problem where: Given the register AX=0425h. Write a program which adds the sum of digits of value 0425h and stores the sum in the same register AX.
I have no idea what to do in it. Can anyone help me solve this thing?
I tried to think of a solution and did not find anything :)
Given the register AX=0425h
The digits of this hexadecimal number are 0, 4, 2, and 5. The assignment wants you to sum these as in 0 + 4 + 2 + 5 = 11.
One possible solution is the following:
mov edx, eax ; -> DH=04h AL=25h
aam 16 ; 25h/16 -> AH=2 AL=5
add al, ah ; (5+2) -> AL=7
xchg al, dh ; -> DH=7 AL=04h
aam 16 ; 04h/16 -> AH=0 AL=4
add al, ah ; (4+0) -> AL=4
add al, dh ; (4+7) -> AL=11
cbw ; -> AX=11
The code works for any value AX=[0000h,FFFFh] producing AX=[0,60].
A solution that uses a loop and that can deal with any value EAX=[00000000h,FFFFFFFFh] producing EAX=[0,120]:
xor ecx, ecx ; TempResult = 0
More:
mov ebx, eax ; Copy to another temporary register where
and ebx, 15 ; we only keep the lowest digit
add ecx, ebx ; TempResult + LowestDigit
shr eax, 4 ; Shift the original digits to the right discarding the one(s) we already added to the TempResult
jnz More ; Only looping if more non-zero digits exist
mov eax, ecx ; EAX = TempResult

Incorrect value of the variable ans that stores the LCM of two numbers (8086 Program)

Following is the code I wrote to find LCM of two numbers in EMU8086. When I ran it, I am getting value 0 in the Ans variable.
.MODEL SMALL
.DATA
Num1 DW 250
Num2 DW 100
Ans DW ?
.CODE
MOV AX,#DATA
MOV DS, AX
MOV AX, Num1
MOV BX, Num2
MOV DX, 0000h
NEXT: PUSH AX
PUSH DX
DIV BX
CMP DX, 0000h
JZ LAST
POP DX
POP AX
ADD AX, Num1
JNC NEXT
INC DX
JMP NEXT
LAST: POP Ans+2
POP Ans
MOV AH, 4Ch
INT 21h
END
LCM(a, b) = a * b / GCD(a, b)
Due to this equation, you can find GCD using Euclid's algorithm and then calculate LCM. Assuming numbers a and b are in al and dl, this code calculate LCM.
; Save multiplication value
MOV AL, DL ; This 2 lines is AL * DH
MUL DH
PUSH AX ; Store result in stack
FINDBCD: ; General idea is : LCM(a, b) = a*b/BCD(a,b)
; We calculate BCD with euclidean algorithm
CMP DH, DL
JNS CALCULATE ; Jump if DL < DH else swap them
MOV CL, DL ; This 3 line swap DL and DH
MOV DL, DH
MOV DH, CL
CALCULATE:
MOV AL, DH ; Move greater number in AL
XOR AH, AH ; Zero AX 8 second bits
DIV DL ; This is AL/DL
CMP AH, 0 ; If remainder is zero so we found BCD
JE AFTERFINDBCD
SUB DH, DL ; Else substract smaller number from greater number
JMP FINDBCD ; Do this until find BCD
AFTERFINDBCD:
CMP DH, DL
JNS FINDLCM ; Jump if DL < DH
MOV CL, DL ; This 3 line swap DL and DH
MOV DL, DH
MOV DH, CL
FINDLCM:
POP AX ; Retreive multiplication result
DIV DL ; This is AX/DL
AND AX, 0000000011111111B ; Ignore remainder

Effective addressing in Real Mode - accessing array

I am working in real mode of x86 and say I need to access a element from the array people; the index of which is in the register BX.
MOV BX, 2
struc person
.name resb 11
.age resb 1
endstruc
people: times 10 db person_size
The effective addressing in real mode is limited to base + offset. So code like
mov [people + bx * person_size + person.age],byte 20
does not work; however the assembler can do the calculation if no BX register is used -
mov [people + 2 * person_size + person.age],byte 20
I can do multiplication or shift lefts a few times and make it work, but is there a way to do access any element in an array, without assuming that the size of the structure will remain the same in future?
Is there any other way than multiplying like below (cannot do shifts if the structure size changes, code will also change)?
push ax
mov ax, person_size
mul bx
mov bx, ax
pop ax
add bx, person.age
mov [people + bx], byte 20
The effective addressing in real mode is limited to base + offset.
Only on 8086 but not on x86-16 in general.
It's true that in Real Mode you can use Scaled Index addressing like in Fifoernik's answer, but in your program it won't help much since the Scale values are limited to either {1, 2, 4, or 8} and your structure has 12 bytes.
You must do the multiplication yourself especially since you want to leave it open what the size of the structure will be in future.
push ax
mov ax, person_size
mul bx
mov bx, ax
pop ax
add bx, person.age
mov [people + bx], byte 20
What the Real Mode on x86-16 does offer is an extra imul variant that simplifies your calculation:
imul bx, person_size
mov [people + bx + person.age], byte 20
There was no need to add person.age in a separate instruction. The assembler will add people and person.age to become a 16-bit offset.
Your version with the mul bx instruction also modified the DX register. you didn't preserve that one like you did with AX!
For a true 8086 your code was (almost) fine:
push ax
push dx
mov ax, person_size
mul bx
mov bx, ax
pop dx
pop ax
mov [people + bx + person.age], byte 20
One optimization would pad the 12-byte structure to 16 bytes.
struc person
.name resb 11
.age resb 1
.pad resb 4
endstruc
This replaces multiplication by simple shifting to the left in order to access the elements:
For x86-16 (array index in ebx):
shl ebx, 1
mov [people + ebx * 8 + person.age], byte 20
or for 8086 (array index in bx):
push cx
mov cl, 4
shl bx, cl
pop cx
mov [people + bx + person.age], byte 20
Another solution uses a lookup table to avoid multiplication and padding.
LUT dw 0, 12, 24, 36, 48, 60, 72, 84, 96, 108 ; 10 elements
...
shl bx, 1 ; Lookup table holds words
mov bx, [LUT + bx] ; Fetch array element's offset
mov [people + bx + person.age], byte 20

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

Comparing AX register against zero

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)

Resources