Curs de instruire

În această parte a cursului de instruire vom analiza elementele de bază ale creării procedurilor. Procedura este un cod care poate fi executat de mai multe ori și poate fi accesat din diferite părți ale programului. De obicei, procedurile sunt concepute pentru a efectua anumite acțiuni separate ale programului și, prin urmare, acestea sunt uneori numite subrutine. În alte limbi de programare, procedurile pot fi numite funcții sau metode, însă, de fapt, ele sunt la fel







Comenzi CALL și RET

Întoarcerea din procedură este efectuată de comanda RET. Această comandă restaurează valoarea din partea de sus a stivei în registrul IP. Astfel, executarea programului continuă cu comanda imediat urmând comanda CALL. În mod normal, codul de procedură se termină cu această comandă. Comenzile CALL și RET nu modifică valoarea steagurilor (cu excepția cazurilor speciale în modul protejat). Un mic exemplu de modalități diferite de a apela o procedură:

Apeluri de proceduri aproape și de departe

Există 2 tipuri de apeluri de procedură. Cel de mijloc este numit apelul procedurii, care se află în segmentul de cod curent. Un apel lung este un apel de procedură într-un alt segment. În consecință, există 2 tipuri de comandă RET - pentru întoarcerea la aproape și la distanță. Compilatorul FASM determină automat tipul de instrucțiune de mașină de care aveți nevoie, deci în majoritatea cazurilor nu trebuie să vă faceți griji.

În cadrul cursului de formare vom folosi doar apeluri de procedură cu rază scurtă de acțiune.

Pe lângă parcurgerea parametrilor, adesea trebuie să obțineți o anumită valoare din procedură. De exemplu, în cazul în care procedura evaluează ceva, aș dori să obțin rezultatul calculului. Și dacă procedura face ceva, este util să știm dacă acțiunea a fost efectuată cu succes sau când a apărut o eroare. Există modalități diferite de a returna o valoare dintr-o procedură, dar cea mai obișnuită modalitate este de a pune valoarea într-unul din registre. De obicei, AL și AX sunt utilizate în acest scop. Deși poți să faci cum dorești.

O bună tehnică este păstrarea registrelor care modifică procedura în timpul execuției. Aceasta vă permite să apelați procedura din orice parte a codului și nu vă faceți griji că valorile din registre vor fi corupte. De obicei, registrele sunt stocate în stivă utilizând comanda PUSH. și înainte de revenirea din procedură sunt restabilite prin comanda POP. Firește, trebuie să fie restaurate în ordine inversă. Aproximativ astfel:

Pentru a trasa o linie orizontală de caractere, se utilizează procedura draw_line. Codul de simbol este transferat pe DL și numărul de caractere care trebuie afișate pe ecran în CX. Această procedură nu aduce nicio valoare. Pentru a scoate 2 caractere de la sfârșitul liniei, se scrie procedura print_endline. Se numește fără parametri și, de asemenea, nu returnează nicio valoare. Codurile de caractere pentru cadrele de desen pot fi recunoscute utilizând tabela de codificare a caracterelor 866 sau puteți utiliza programul standard "Tabela de caractere" Windows selectând fontul Terminal.

Rezultatul programului arată astfel:

Curs de instruire

Debugger de depanare a programului Turbo

Aici, se pare că funcționează. Doar acest lucru este ceea ce gunoi: atunci când se împarte, întregul este obținut în ah, iar restul este un întreg în dx, și cf. aritmetică - număr întreg + fracție. Și cum să o obțin, nu știu.
utilizare16
org 100h
jmp start
; --------
array1 dw 1,2,3,4,5,6,6
array2 dw 2,5,6,8,9
array3 dw 5,5,7,6,8,8
n1 dw 7






n2 dw 5
n3 dw 6
sr1 rw 1
sr2 rw 1
sr3 rw 1
; ----------
începe:
mov cx, [n1]
mov bx, array1
sunați sr_arifm
mov [sr1], ax

mov cx, [n2]
mov bx, array2
sunați sr_arifm
mov [sr2], ax

mov cx, [n3]
mov bx, array3
sunați sr_arifm
mov [sr3], ax
jmp quit

sr_arifm:
xor di, di
xor si, si
xor ax, ax
xor dx, dx
mov si, cx

lp:
adăugați axa, [bx + di]
adaugă di, 2
jcxz întoarcere
buclă lp
întoarce:
div si
putrezi

renunțe la:
mov ax, 4c00h
int 21h

În mod corect, credeți că partea fracționară este eliminată atunci când împărțiți numerele întregi, astfel încât restul să fie ignorat.
Programul este scris corect. Singurul lucru este că nu puteți reseta registrul SI în procedură (dar știu că este mai convenabil pentru dvs.). Și comanda JCXZ este inutilă, nu va exista niciodată un salt, deoarece în interiorul ciclului CX nu este zero.

Da, cu jcxz peremudril))) la sfârșitul ciclului, divizarea va fi încă efectuată.
Vă mulțumim pentru următoarea lecție.

Trebuie să existe un registru BX.

În continuare în interiorul funcției, registrul AX trebuie resetat. În caz contrar, suma este considerată incorectă. Și cu al doilea apel, elementele matricei vor fi adăugate la media aritmetică a primei matrice.

În exemplele anterioare, problema dimensionalității sumei elementelor unei matrice de cuvinte care este limitată la 65 535 * 65 535 nu este luată în considerare. cuvânt dublu.
Și a fost interesant de a pune rezultatele pe stivă.
utilizare16
org 100h
jmp start

începe:
mov bx, array1
mov cx, [arl1]
sunați sr_ar
mov bx, array2
mov cx, [arl2]
sunați sr_ar
mov bx, array3
mov cx, [arl3]
sunați sr_ar

mov ax, 4C00h
int 21h

Totul pare a fi corect
Obțineți acea procedură pentru a returna rezultatul prin stivă. Nu este mai ușor să părăsiți AX-ul?

O bucată frumoasă de cod:

Un program bun, dar există un neajuns.

Dacă rezultatul este returnat la AL, atunci stocarea întregului AX este incorectă. În AH va exista o rămășiță de diviziune.
Trebuie să adăugăm

înainte de fiecare salvare a rezultatului. Deoarece numărul este nesemnificat. Sau adăugați la sfârșitul procedurii, atunci rezultatul va fi în AX.

De asemenea, puteți elimina o comandă MOV din procedură dacă numărul de elemente este transferat în CX sau DX.

utilizare16
org 100h
jmp start

array1 dw 12, 8456, 0
lungime 1 dw 3
array2 dw 56875, 1546, 154, 84, 6
lungime2 dw 5
array3 dw 156, 8974, 1548, 7895
lungime 3 dw 4

începe:
mov bx, array1
mov cx, [lungime1]
sunați sr_arifm
mov bx, array2
mov cx, [length2]
sunați sr_arifm
mov bx, array3
mov cx, [lungime3]
sunați sr_arifm

mov ax, 4C00h
int 21h

sr_arifm:
împingeți cx
xor si, si
xor ax, ax
lp:
adăugați axa, [bx + si]
adaugă și, 2
buclă lp
pop cx
div cx
putrezi
El consideră corect numai prima dată.
Nu-mi spuneți ce este greșeala?

Trebuie să zero DX-ul înainte de comanda DIV.
Deoarece divizorul este un registru pe 16 biți, dividendul este în DX: AX.
În timpul primei divizări în DX, zero, iar în timpul celui de-al doilea și al treilea - există un rest din diviziunea precedentă.
În caz contrar, totul este corect

[Cod]
utilizare16
org 100h
jmp Start

; ======================== [date] ==
arrA dw 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5
arrAlen db 6

arrB dw 0xB0, 0xB1, 0xB2, 0xB3, 0xB4
arrBlen db 5

arrC dw 0xC0, 0xC1, 0xC2, 0xC3
arrClen db 4

media rw 3; arrA = 0xA2, arRB = 0xB2, arRC = 0xC1
; ================================
Start:
xor si, si

mov bx, arrA
movzx cx, [arrAlen]
sunați la get_average
mov [medie + si], ax

inc si
mov bx, arrB
movzx cx, [arrBlen]
sunați la get_average
mov [medie + si], ax

inc si
mov bx, arrC
movzx cx, [arrClen]
sunați la get_average
mov [medie + si], ax

mov ax, 4c00h
int 21h

; ----------
get_average :; bx == arrN, cx == count, ax == rezultatele returnate ale mediei
xor ax, ax
împingeți cx
calc:
adăugați axa, [bx]
adăugați bx, 2
buclă calculată
pop cx
chemați diviziunea
putrezi

diviza :; ax == împărțit, cx == divisor, ax == rezultat
CWD
div cx
putrezi
[/ code]

Nicio eroare, dar împărțirea în zadar a făcut o procedură separată.

Puteți elimina pur și simplu comanda de divizare a apelului și ret după ea - va funcționa, de asemenea

jmp principal
; =====================================;
arr1 dw 834,536,1744,6890,241
arr1_length db 5
arr2 dw 486,6574,19,684
arr2_length db 4
arr3 dw 8577985,911,648,6578,9894,245
arr3_length db 7
; =====================================;
principale:
xor cx, cx
CLD
mov si, arr1
mov cl, [arr1_length]
call getAvrg; axa este media aritmetică a lui arr1
mov si, arr2
mov cl, [arr2_length]
call getAvrg; ax - aritmetic mean ar2
mov si, arr3
mov cl, [arr3_length]
call getAvrg; ax - aritmetic mean ar3

O procedură bună și comenzile lodsw, sunt folosite sunt relevante.
Aici există doar o obligație legată de faptul că dimensiunea matricei este în octet după matricea însăși. Dacă uitați de asta, puteți obține o greșeală greu de găsit.
Prefer să salvez cx de la început cu comanda POP, apoi să îl restaurez cu comanda PUSH înainte de împărțire.

Sunt de acord. Și adăugați la începutul procedurii







Articole similare

Trimiteți-le prietenilor: