Re: [Асемблер8086]Від нуба до професіонала(або шлях на асемблерну вершину)
можна і бітами здвигу бітів в право і ліво застосувати
Ви не увійшли. Будь ласка, увійдіть або зареєструйтесь.
Ласкаво просимо вас на україномовний форум з програмування, веб-дизайну, SEO та всього пов'язаного з інтернетом та комп'ютерами.
Будемо вдячні, якщо ви поділитись посиланням на Replace.org.ua на інших ресурсах.
Для того щоб створювати теми та надсилати повідомлення вам потрібно Зареєструватись.
Український форум програмістів → Системне програмування → [Асемблер8086]Від нуба до професіонала(або шлях на асемблерну вершину)
можна і бітами здвигу бітів в право і ліво застосувати
А ще існує команда lea, яка зазвичай рахує зсув, але може обмежено обчислювати прості арихметичні вирази. Наприклад, lea ax, [bx+1] додасть до bx регістру одиницю та одержаний резутьтат запише у ax
0xDADA11C7 випередив
кому цікаво як виправити
поміняти місцями 42-43 рядки
MOV WORD PTR SUM, CX
MOV WORD PTR SUM+2, DX
на дебагері включити розмір для змінних dword
SUM=(X1+X2+X3)^2-15*C1
;Calculate SUM=(X1+X2+X3)^2-15*C1
data segment
X1 DB 250d
X2 DB 199d
X3 DB 12d
C1 DB 84d
SUM DD ?
ends
code segment
start:
; set segment registers:
mov ax, data
mov ds, ax
;X1+X2+X3
MOV AL, X1
MOV AH, 0
MOV BL, X2
MOV BH, 0
ADD AX, BX
MOV BX, AX
MOV AL, X3
MOV AH, 0
ADD AX, BX
;^2
MUL AX
MOV CX, AX
;15*C
MOV AL, 15d
MOV BL, C1
MUL BL
;EXPRESSION1-EXPRESSION2
SUB CX, AX
;SUM
MOV WORD PTR SUM, CX
MOV WORD PTR SUM+2, DX
mov ax, 4c00h ; exit to operating system.
int 21h
ends
end start ; set entry point and stop the assembler.
Розгалуження
Задача
y = {
a*b+c, if x=0
a+b*c, if x=2
(a+b)*c, if x!=0 and x!=2
}
Розв'язок
(крім "х" бажано нічого не міняти, якщо будуть великі числа програма працюватиме не правильно, звичайно це можна виправити, але мені лінь)
data segment
Y DW ?
X DB 1
A DB 4
B DB 5
C DB 8
ends
code segment
start:
; set segment registers:
mov ax, data
mov ds, ax
;A*B+C, X=0
;A+B*C, X=2
;(A+B)*C, X!=0 AND X!=2
CMP X, 0
JE X0
CMP X, 2
JE X2
JMP XN0N2
X0:
;A*B+C, X=0
MOV AH, 0
MOV AL, A
MUL B
MOV BH, 0
MOV BL, C
ADD AX, BX
MOV Y, AX
JMP IFEND
X2:
;A+B*C, X=2
MOV AH, 0
MOV AL, B
MUL C
MOV BH, 0
MOV BL, A
ADD AX, BX
MOV Y,AX
JMP IFEND
XN0N2:
;(A+B)*C, X!=0 AND X!=2
MOV AH, A
MOV AL, B
ADD AL, AH
MUL C
MOV Y, AX
IFEND:
mov ax, 4c00h ; exit to operating system.
int 21h
ends
end start ; set entry point and stop the assembler.
до речі, для тих хто хоче вивчати асемблер, не потрібно вчити таблицю переходів
Достатньо зрозуміти логіку.
Спочатку йде J(JUMP)
потім йде N(NOT) - якщо заперечення
ще дальше йде L(LESS) - менше, G(GREATER) - більше; - для цілих чисел зі знаком
A(ABOVE) - менше, B(BELOV) - більше; - для цілих чисел без знаку
і в кінці E(EQUAL) - рівне
тобто виходить така конструкція Jx(заперечення)x(більше/менше)x(рівне)
Або, якщо порівняння йде з флагами (flag), то пишеться перша буква флага.
Є тільки одне виключення JCXZ
Саме так, варто вчити лише прапорці процесора, а ота таблиця сама потім в голову сяде.
Цикли
Серед 100 заданих чисел визначити кількість додатних, від'ємних і нульових.
(сто чисел заданих немає, але я думаю код буде працювати і при 100 )
data segment
ARR1 DB 10, 81, 0, -9, 5, 0, -8, 0, 0, -9, -10, 121, 44
SIZE_OF_ARR1 DB $-ARR1
NUMADD DB 0
NUMSUB DB 0
NUMZERO DB 0
ends
code segment
start:
; set segment registers:
mov ax, data
mov ds, ax
MOV CH, 0
MOV CL, SIZE_OF_ARR1
LOOPBEGIN:
MOV SI, CX
DEC SI
CMP ARR1[SI], 0
JL NUMLESS
JG NUMGREATER
;EQUAL ZERO
MOV AL, NUMZERO
INC AL
MOV NUMZERO, AL
JMP IFEND:
;MORE THAN ZERO
NUMGREATER:
MOV AL, NUMADD
INC AL
MOV NUMADD, AL
JMP IFEND:
;LESS THAN ZERO
NUMLESS:
MOV AL, NUMSUB
INC AL
MOV NUMSUB, AL
IFEND:
LOOP LOOPBEGIN
mov ax, 4c00h ; exit to operating system.
int 21h
ends
end start ; set entry point and stop the assembler.
зараз ще три задачки скину...
Задача - перевернути число. (12345-54321)
Мала бути задача на цикли, але щось не вийшло робити через цикли. Мені не зручна умова CX=0, тому я не використовував LOOP, LOOPE, LOOPNE і JCXZ. Я зробив цикли через розгалуження, логіка така:
В кінці, коли вже буде ділитися останнє число (наприклад 5/10 = 0 - частка і 5 - залишок), перевіряється частка (тобто АХ), очевидно якщо частка = 0 значить "число закінчилося" і пора виходити з розгалуження.
Також, додатково, я використав розгалуження-цикл для знаходження степеня. Можливо в емуляторі 8086 є бібліотеки для математики, але я поки що вивчаю синтаксис, вже на MASM32 буду пробувати використовувати бібліотеки (якщо вони існують).
Загалом алгоритм такий
- є число input - вхідне
- є число output - вихідне
- є число i = 1
q - частка, r - залишок = input/10;
output=output * 10^i + q;
i++;
input = q;
поки q!=0
Програма
data segment
INPUT DW 12345
OUTPUT DW 0
ends
code segment
start:
; set segment registers:
mov ax, data
mov ds, ax
MOV CX, 1
MOV AX, INPUT
LOOPBEGIN:
MOV BX, 10
DIV BX ;AX/10
PUSH AX ;SAVE REMAINDER
PUSH DX ;SAVE QUOTIENT
PUSH CX ;SAVE COUNTER
;10 ^ INCREMENT
MOV AX, 0
LOOPBEGIN2:
CMP CX, 1
JE CONTINUE
MOV AX, 1
MUL BX
DEC CX
JMP LOOPBEGIN2
CONTINUE:
MOV BX, AX
POP CX ;GET COUNTER
MOV AX, OUTPUT
;OUTPUT * (10 ^ INCREMENT) + QUOTIENT
MUL BX
INC CX ;INC++
POP BX ;GET QUOTIENT
ADD AX, BX
MOV OUTPUT,AX
POP AX ;GET REMAINDER
;WHILE AX!=0
CMP AX, 0
JE LOOPEND
JMP LOOPBEGIN
LOOPEND:
mov ax, 4c00h ; exit to operating system.
int 21h
ends
end start ; set entry point and stop the assembler.
Можливо в емуляторі 8086 є бібліотеки для математики
Є одна така, FPU називається
Колись ми її на форумі трохи обговорювали http://replace.org.ua/post/42089/
Питання про "чергу", чи правильну організацію "стеку"
є такий код
PUSH CRAMER[SI]
PUSH CRAMER[SI+2]
PUSH CRAMER[SI+4]
PUSH CRAMER[SI+8]
PUSH CRAMER[SI+10]
CALL DETERMINANT3X3
mov ax, 4c00h
Я хочу зробити так, щоб CALL записався самим першим у стек (тобто адрес на наступну команду + зміщення)
PUSH адрес команди "mov ax, 4c00h"
PUSH CRAMER[SI+2]
PUSH CRAMER[SI+4]
PUSH CRAMER[SI+8]
PUSH CRAMER[SI+10]
виклик функції без запису в стек DETERMINANT3X3
Так можна зробити ?
Ви хочете виконати команду call не виконуючи команду call?
PUSH CRAMER[SI]
PUSH CRAMER[SI+2]
PUSH CRAMER[SI+4]
PUSH CRAMER[SI+8]
PUSH CRAMER[SI+10]
CALL DETERMINANT3X3
mov ax, 4c00h
Як би я зробив
PUSH CRAMER[SI]
PUSH CRAMER[SI+2]
PUSH CRAMER[SI+4]
PUSH CRAMER[SI+8]
PUSH CRAMER[SI+10]
PUSH OFFSET END_OF_PROGRAM
JMP DETERMINANT3X3
END_OF_PROGRAM:
mov ax, 4c00h
Для початку:
PUSH щось еквівалентно SUB SP, розмір чогось + MOV [SP], щось.
POP щось еквівалентно MOV щось, [sp] + ADD SP, розмір чогось
CALL X еквівалентно PUSH NEXT_INSTRUCTION + JMP X, де NEXT_INSTRUCTION - адреса наступної команди
RET еквівалентно POP IP
Стек викликів використовується для зберігання:
- адрес повернення з функцій, тому так і зветься
- збереження значень регістрів
PUSH BP ;нам цей BP ще знадобиться
MOV BP, ... ;якась робота, що змінює BP
...
POP BP ; відновлюємо значення BP
- зберігання локальних змінних
MOV BP, SP;тепер BP вказує на верхівку стека
PUSH AX ;"створюємо змінну", частіше просто відніманням від SP потібної кількості байтів
MOV [BP-2],2 ;BP-2 - адреса нашої змінної, поки ми не змінимо BP
Якщо функція з локальними змінними буде викликана рекурсивно, то вона створить собі нову змінну.
- передачі параметрів у функцію
Працює так само, як і з локальними змінними, тільки треба чітко домовитися, де саме вони будуть розташовані і які регістри можуть бути змінені, а які - ні. Оскільки існує інструкція CALL, яка, вочевидь, має бути останньою перед початком роботи функції, то природно робити так:
PUSH аргумент
CALL функція
POP аргумент ; або просто ADD SP,2 - викидаємо зі стеку аргументи функції
функція:
PUSH BP ; BP знадобиться функції, що нас викликала
MOV BP, SP ; в стеку зараз: зовнішній BP = [BP], адреса повернення = [BP+2], аргумент = [BP+4]
... ;працюємо
POP BP ; відновлюємо BP
RET
Хоча ніхто не може заборонити вам зробити власний стандарт виклику функцій, заміняти PUSH, POP, CALL і RET на аналоги і викликати що вам хочеться:
PUSH адреса_повернення
PUSH аргумент
JMP функція
адреса_повернення: ...
функція:
...
POP аргумент ;викидаємо аргумент
RET ; відновлюємо адресу IP на наступну команду після виклику функції
ще
CALL [ESP]
еквівалент
RET
ще
CALL [ESP]
еквівалент
RET
CALL - це PUSH+JMP, а RET - це POP+JMP. Так що не виходить. Ви краще Лінукс критикуйте, там без конкретики у вас краще виходить.
коли йде використання початок стекового фрейму, а приблизно так виглядає:
адресс: байт код
адресс: call [adress} // перехід по адресу, і якщо буде повернення назад в разі успішного повернення, то в стеку буде, адреса що за call інструкцією виконується ret, це святе в пошуку по переповнення буфера.
не вірю словам теж, по можливості загляну в отладчик
коли йде використання початок стекового фрейму, а приблизно так виглядає:
адресс: байт код
адресс: call [adress} // перехід по адресу, і якщо буде повернення назад в разі успішного повернення, то в стеку буде, адреса що за call інструкцією виконується ret, це святе в пошуку по переповнення буфера.не вірю словам теж, по можливості загляну в отладчик
Ви початківцю пишете не забувайте, а оце бурмотіння з неправильним синтаксисом й наполовину видране з дампу стека знежучувача з додаванням святих пошуків в переповненні бухвера початківець не розпарсить.
Ви початківцю пишете не забувайте
я ж не пишу, про інклуд dll бібліотеки і це початківцю.
коли буде цікаво, колись згадає і згодиться
Як записати в стек додатне число?
PUSH 00A6h
Як не пробую, всеодно в стеку опиняється "FFA6"
це не проблема стеку - це проблема вашого сприйняття стеку, гадаю ви просто не туди дивитесь. спробуйте пересвідчитись в цьому за допомогою всунення в стек 0A6h і діставання в якийсь регістр загального призначення - наприклад, аумулятор ax