Процедурада регистрлерді сақтау
Мазмұны
9.2.1.Подпрограмманы қай жерге орналастыру керек. 1
9.2.2. Подпрограмманы қалай безендіреді 2
9.2.3.Процедураларды шақыру және олардан қайту. 3
9.2.4. CALL командасының басқа варианттары. 7
9.3. Регистр арқылы параметр беру. 8
9.3.1. Мәндер бойынша параметрлерді беру. 9
9.3.2. Жөнелту арқылы параметрлер беру. 9
9.3.3. Процедурада регистрлерді сақтау. 11
9.3.4. Қиын түрдегі параметрлерді беру. 12
9.4. Стек арқылы параметрлерді беру. 13
9.5. Процедуралардың жергілікті берілгендері. 18
9.6. Рекурсивті процедуралар. 21
10 ТАРАУ. Берілгендердің динамикалық құрылымдары. 25
10.1. Жолдық командалар. 25
10.1.1. Жолдарды салыстыру командасы. 26
10.1.2. Қайталау префикстері. 29
10.1.3. Басқа жолдық командалар. 34
9.2.1.Подпрограмманы қай жерге орналастыру керек.
Бірінші проблема – ол қай жерге подпрограмманы орналастырамыз? Жалпы
айтканда кез-келген жерде. Бірақта сонымен қатар подпрограмма өзінен өзі
орындалмауы керек, ал тек оған жолдағанда ғана орындалуы тиіс екенін түсіну
керек. Соңдықтан оларды байқаусыздан басқару оларға көшпегендей орналастыру
керек. Егер бірнеше подпрограмма болса, әдетте оларды қатар орналастырады.
Әдетте подпрограмманы (nn) не FІNІSH командасынан кейін командалар
сегментінің соңына (қар а сурет) не бағдарлама орындала басталатын
команданың алдында, яғни осы сегментінің ең басында (б-суреті). Үлкен
бағдарламаларда подрпаграммаларды көбінесе бөлек командалар сегметінде
орналастырады (в-суретің қар)
С SEGMENT С SEGMENT С1SEGMENT
BEG: ...
FІNІSH
BEG: ... C1 ENDS
С SEGMENT
C ENDS C ENDS BEG: ...
END BEG END BEG ... .
a) б) C ENDS
END BEG
в)
9.2.2. Подпрограмманы қалай безендіреді
Екінші проблема ол подпрограмманы қалай сипаттау керек? Жалпы
жағдайда подпрограмма құрайтын командалар тобын бағдарлама тексінде ешқалай
белгілемеуге болады. Бірақта ЯА-да подграммаларды әдейі безендіру
бекітілген. Ол процедуалар түрінде. Ол белгілі бір пайда келтіреді. Бірақта
ол туралы кейіңірек. Алдағы уакытта біз тек процедура ретінде сипатталған
подрограммаларды қолданамыз.
Подпрограммаларды процедура түрінде сипаттау былай болады:
процедура есіміPROCпараметр
процедура денесі
процедура есіміENDP.
Көріп отырғандарыңыздай процедура денесінің оның командаларының
алдында РRОС procedure директивасы қойылады, ал одан кейін -ENDP
директивасы end of procedure. Бұл қос директивада да бір есім
көрсетіледі. Ол есім болып табылады. РRОС директивасында есімнен кейін қос
нүкте қойылмайтынына көңіл бөлу керек, бірақта ол есім белгі болып
саналады. Ол процедуранын бірінші командасына бағытталған деп саналады.
Мысалға, процедура есімін көшу командасында көрсетуге болады, онда
процедураның бірінші командасына көшу жасалады.
РRОС директивасында параметр болмауы да мүмкін, онда ол NEAR-ға тең
деп саналады. осыған байланысты көбінесе NEAR параметрі көрсетілмейді
NEAR параметрінде не параметр жоқ болғанда процедура жақын, ал FAR
параметрінде –алыс болады. Жақын процедураға тек ол сипатталған командалар
сегментінен жолдауға болады. Ал басқа сегменттерден болмайды, ал алыс
процедураларға кез-келген сегменттен жолдануға болады. Жақын және алыс
процедураларының айырмашылығы тек осында.
ЯА-да процедурада сипатталған есімдер мен белгілер оның ішінде
жерлігіктенбейді, сондықтан олар уникальды болуы тиіс, яғни басқа
есімдермен сәйкес келмеуі тиіс. ЯА-да бір процедураны екінші процедура
ішінде сипаттауға болса да, ол ешқандай пайда бермейді, сондықтан ЯА-да
процедураларды бір-біріне салу қолданылмайды.
9.2.3.Процедураларды шақыру және олардан қайту.
Келесі проблема – ол процедураларды қалай шақыру және олардан қалай
қайту? Жоғары деңгейдегі тілге программалағанда процедураны жұмысқа қосу
үшін тек оның есімін және параметрлерін жазу жеткілікті. Сонымен қатар
процедура жұмысын қалай бастайды, басқаруды негізгі бағдарламаға қалай
қайтаратыны бізді ойландырмайды: осының барлығы үшін транслятор жауап
береді. Ал егер біз ЯА-да бағдарлама жасасақ, онда негізгі бағдарламамен
процедуралар арасындағы көшулерді өзімізге жасуға тура келеді. Ол қалай
жасалатынын қарастырайық.
Мұнда екі проблема: негізгі бағдарламадан процедураны жұмыс істетуге
болады және процедурадан негізгі бағдарламаға қалай қайту. Бірінші проблема
қарапайым түрде шешіледі: процедураның бірінші командасына көшу жасасақ
жеткілікті, яғни көшу командасында процедура есімін көрсетсек жеткілікті.
Екінші проблема қиындау. Себебі процедураға негізгі бағдарламаның кез-
келген жерінен жолдауға болады, және сондықтан процедурадан әртүрлі жерге
қайту керек. Процедураның өзі басқаруды қай жерге қайтару керек екенін
білмейді, бірақта оны негізгі бағдарлама біледі. Сондықтан процедураға
жолдағанда негізгі бағдарлама міндетті түрде қайтатын адресті – процедура
өз жұмысын бітіргеннен кейін көшу жасауға міндетті негізгі бағдарлама
командасының адресін хабардар ету керек. Әдетте бұл процедураға жолдау
командасынан кейінгі команда. Нақты осы адресті негізгі бағдарлама
процедураға хабарлайды, нақты осы адрес бойынша процедура негізгі
бағдарламаға қайтады.
Процедураға әртүрлі жерден жолдау жасағандықтан және қайту адресі
әртүрлі көрсеткендіктен, басқаруды негізгі бағдарламаның әртүрлі жерінен
қайтарады.
Қайтару адресін қалай хабардар етеді? Оны әртүрлі жолдармен жасауға
болады. Біріншіден, регистр арқылы беріп жіберуге болады: негізгі
бағдарлама қандайда бір регистрге қайтару адресін жазады, ал процедура оны
сол регистрден алып сол бойынша көшу жасайды. Екіншіден, стек арқылы
жасауға болады: процедураға жолданбастан бұрын негізгі бағдарлама қайтару
адресін стекқа жазады, ал процедура оны содан алып сол бойынша көшу
жасайды. ПК-да қайтару адресі стек арқылы беру бекітілген, сондықтан алдағы
уақытта біз қайтару адресін берудің тек осы жолын қарастырамыз.
Қайтару адресін стек арқылы беру және осы адрес бойынша қайтаруды біз
білетін командалар көмегімен жүзеге асыруға болады. бірақта реал
бағдарламаларда процедуралар өте жиі қолданылады, сондықтан ПК командалар
жүйесіне негізгі бағдарлама мен процедуралар арасындағы көшулерді жасауды
оңайлататын әдейі командалар қосылған. Олар CALL және RET командалары. Бұл
командалардың негізгі варианттары келесі:
процедураны шақыру (қайтарымды көшу): CALLпроцедура
процедурадан қайту (return):RET
CALL командасы стекқа өзінен кейін келетін команданы жазады және содан
кейін келетін команданы жазады және содан кейін көрсетілген процедураның
бірінші командасына көшу жасайды. RET командасы стек шынынан адресті оқып
көшуді сол бойынша жасайды.
Келесі мысалда қарастырайық. Біз өз бағдарламамызды реттелік делік
және ол үшін оның әртүрлі жеріне х және у бірдей айнымалылардың реттеуші
жазуларын қоямыз. Мұндай жазулар бірнеше командамен жүзеге асады, олар
бірнеше рет қайталанады, сондықтан бұл жазуды процедура ретінде сипаттап
негізгі бағдарламада оған тек қысқа жолдаулар көрсету мәнді болады:
;негізгі бағдарлама ;процедура
. . . PR PROC
CALL PR OUTІNT X
(a) . . . . OUTІNT Y,8
. . . . NEWLІNE
CALL PR RET
(b) . . . . PR ENDP
Бірінші CALL командасы орындалғанда ол өзінен кейін келетін команданың
адресін стекқа жазып басқаруды PR процедураның басына - OUTІNT X
командасына береді. Процедура жұмыс істей бастайды: ол Х және У-ті жазып
жолды тасымалдайды. Содан кейін RET командасы стектағы адресін алып сол
бойынша көшу жасайды. Осылайша бірінші CALL командасынан кейін келетін
командадан негізгі бағдарлама жұмысы қайта басталады. Процедураға кезекті
жолдау осылайша жасалады, бірақта екінші CALL командасы бойынша стекқа
басқа адрес – в адресі жазылады, сондықтан процедура осы жолы басқаруды
негізгі бағдарламаның басқа жеріне – екінші CALL командасынан кейін келетін
командаға.
Енді CALL және RET командаларға қатысты кейбір нақтылаулар жасайық.
ПК-да келесі болып орындалатын команда CS және ІP регистлерімен
берілейтінің айтып кетейік. Егер процедура сипаттамасыз біз оған жолданатын
командалар сегментінде орналасса, онда оған көшу және одан қайту жақын
болуы тиіс, яғни тек ІP команда көрсеткіші ғана өзгеруі тиіс. Өйткені біз
әрдайым бір командалар сегментінде қаламыз. Ал егер процедура басқа
командалар сегментінде орналасса, онда оған көшу және одан қайту алыс болуы
тиіс, яғни қос регистрде өзгеруі тиіс.
Осының барлығы ескеріледі және іс жүзінде ПК-да CALL және RET
машиналық командалардың екі-екі варианттары бар. Егер АВ арқылы қайту
адресі – CALL командасынан кейін келетін команда адресін белгілесек, онда
CALL Р командасының, мұндағы Р-процедура есімі, қос вариантында былай
сипаттауға болады:
жақын қайтару: стек ( ІP
алыс қайтару: стек( ІP, стек CS.
CALL және RET командалары келісімді жасалуы тиіс екені түсінікті:
процедураға шақыру жақын болғанда одан қайту да жақын болуы тиіс, ал шақыру
алыс болғанда қайту да алыс болуы тиіс, кері жағдайда бағдарлама дұрыс
жұмыс істемейді. Сонымен қатар ЯА-да әрбір команданың қос варианттары
бірдей жазылады әрине, онда ЯА-да негізгі бағдарлама мен процедура арасында
көшу түрлері қалай көрсетіледі екен деген сұрақ туындайды? Жауабы мындай:
ол нақты көрсетілмейді, бұл проблеманы біз үшін ассемблер шешеді. Егер біз
процедураны жақын ретінде сипаттасақ онда осы процедура есімі көрсетілген
барлық CALL командаларын ассемблер жақын шақыру машиналық командаларына
жөнелтеді, ал барлық осы процедураның ішінде орналасқан RET командаларын
ассемблер жақын қайтару машиналық командаларға жөнелтеді. Егер процедура
алыс болып сипатталса, онда барлық оған жолдаулар алыс шақыру ретінде ал
оның ішіндегі барлық RET командалары – алыс қайтарулар ретінде жөнелтіледі
(процедуралар сыртында RET жақын қайтару ретінде қарастырылады). Осылайша
CALL және RET командалар арасындағы сәйкестікті қарауды ассемблер өзіне
алады, ал біздің оған басымыз ауырмауы тиіс. Подпрограммаларды процедуралар
түрінде жазудың нақты пайдасы осында, нақты осы себепті ЯА-да
подпрограммаларды процедура ретінде сипаттау бекітілген.
Бірақта мұнда бір құлық бар. Ассемблер CALL командасы үшін дұрыс
вариантты тек егер процедура осы командаға дейін сипатталса ғана таңдайды.
Егер процедура кейін сипатталатын болса, онда ассемблер бұл процедураның
түрін (жақын не алыс) білмей CALL командасын кездестіргенде оны жақын деп
санап (көбінесе солай болады) әрқашан жақын шақырудың машиналық командасын
жасайды. Ал егер одан кейін ассемблер берілген процедура алыс екенін біосе,
қате деп табады. Мұндай қателік жібермес үшін CALL командасында PTR
операторы көмегімен процедура алыс екенін нақты көрсету керек:
CALL ҒАR PTR Р
9.2.4. CALL командасының басқа варианттары.
Біз CALL командасының негізгі варианттары – оның операнды ретінде
процедура есімі көрсетілетін жағдайды қарастырдық. Бірақта операндтың JMP
шартсыз көшу командасындағыдай SHORT операторымен жағдайдан басқа, басқа да
варианттары мүмкін.
Мысалдар:
NA DW P
FA DO Q
. . .
P PROC
. . .
P1: . . .
. . .
P ENDP
Q PROC FAR
. . .
Q1: . . .
. . .
Q ENDP
. . .
CALL P1; P1-ге
CALL FAR PTR Q1; Q1-ге қайтымды алыс көшу
CALL Q1; Q1-ге қайтымды жақын (!) көшу
CALL NA; P процедурасын жақын шақыру
CALL FA; Q процедурасын алыс шақыру
LEA BX, Q
CALL [BX]; Q процедурасын жақын (!) шақыру
CALL QWORD PTR [BX]; Q процедурасын алыс шақыру
CALL командасының бұл варианттарын қолданғанда абайлық сақтау керек.
Біріншіден, қайтарымды көшу көшу түрінің RET командасы бойынша қайтару
түрімен келісімділігін байқау керек, өйткені бұл жағдайларда ассемблер
мұндай келісімділікке жауап бермейді. Екіншіден JMP командасындағыдай CALL
командасында алға жөнелту немесе жанама жөнелтулерде егер керек болса
жөнелту түрлерін нақтылау керек (үндемеушіліктен алға жөнелтуде ассемблер
тура шақыруды шығарады, ал жанама жөнелтуде жақын жанама шақыруды).
9.3. Регистр арқылы параметр беру.
Енді процедура параметрлерімен байланысты проблемаларды қарастырайық.
Жоғары деңгейдегі тілдерде процедуралар үшін параметрлерді беру үшін
оларды процедураларды шақыру операторында жазу жеткілікті. ЯА-да параметр
беру проблемасы жеңіл емес, сондықтан біз оны нақтырақ қарастырамыз.
Сонымен қатар процедура нәтижесі қалай қайтарылатынын қарастырамыз. ЯА-
да процедура мен функцияға формалды бөлулер жоқ, екеуінде процедуралар деп
аталады. Бірақта оларға қатысты оларды функцияға және таза процедураларға
бөлуге болады. олар нәтиже шығарма жақ екеніне байланысты.
Процедураларға параметрлерді әртүрлі жолдармен беруге болады. Ең
қарапайым тәсіл – ол параметрлерді регистр арқылы беру: негізгі бағдарлама
параметрлерді қандайда регистрлерге жазады, ал процедуралар одан кейін оны
өз жұмысында регистрден алып қолданады. Осылайша егер нәтиже болса, жасауға
болады: процедура өз нәтижесін бір регистрге жазады ал негізгі бағдарлама
содан кейін регистрден нәтижені алады. Параметрлермен нәтижелерді қандай
регистр арқылы беру ол бағдарлама авторының өз қалауы регистрлерді өзі
анықтайды.
9.3.1. Мәндер бойынша параметрлерді беру.
Мындай мысал қарастырайық. c=max(a,b)+max(5, a-1)-ны есептеу керек,
мұндағы барлық сандар таңбалы және сөз өлшемді. max(х,у)-ті есептеуді
процедура-функция ретінде сипаттаймыз, сонымен қатар келесі жайлы
келісейік: негізгі бағдарлама бірінші параметрді (х) АХ регистрі арқылы
беру керек, екінші параметрді (у) ВХ регистрі, ал нәтижені (max) процедура
АХ регистрі арқылы қайтаруы тиіс. Бұл шарттарда процедура және негізгі
бағдарламаның фрагменті былай көрінеді.
;процедура: АХ=max(АХ,ВХ) ;негізгі программа
MAX PROC FAR . . .
CMP AX, BX MOV AX, A ;AX:=a
JGE MAX1 MAX BX, B ;BX:=b
MOV AX, BX CALL MAX ;AX:=max(a,b)
MAX1: RET MOV C, AX ;AX-ті құтқару
MAX ENDP MOV AX, S ;AX:=S
MOV BX, A
DEC BX ;BX:=a-1
CALL MAX ;AX:=MAX(5, a-1)
ADD C, AX ;C:=MAX(a,b)+MAX(5, a-1)
Егер Паскаль тілінің терминологиясын пайдалансақ, онда бұл мысалда
параметрлер мәндер арқылы беріледі: процедураға жолданбастан бұрын негізгі
процедура параметр мәндерін есептеп осы мәндерді регистрлерге жазады. Ал
енді параметрлерді берудің басқа тәсілі – жөнелту арқылы тәсілін
қарастырайық.
9.3.2. Жөнелту арқылы параметрлер беру.
Паскаль тілінде келесі процедураны алайық:
Produce D(var x:іnteger); begіn x:=xdіv 16 end;
Программада оларға мындай жолдау болсын: D(А) және D(В), мұндағы А
және В – мәндері теріс емес сандар болып табылатын айнымалылар есімі.
Көріп отырғандарыңыздай процедура өз параметрлеріне бірдеңе атайды.
Машиналық тіл терминдерінде атау зерденің қандайда бір ұяшығына жазуды
білдіреді, ал ұяшыққа бірдеңе жазу үшін бұл ұяшықтың адресін (есімін) білу
керек. Сондықтан процедура жазу жазуы керек ұяшықтың (А не В) адресін білу
керек, ал оны негізгі бағдарлама хабардар етуге міндетті Осылайша
параметрлерді жөнелту арқылы беру сәйкес параметрге ұяшық адресін (есімін)
беруді білдіреді.
Адресті қалай беру керек? Регистр арқылы: негізгі бағдарлама қандайда
бір регистрге айнымалының адресін жазады, ал процедура оны сол регистрден
алады. Ол қандай регистр? Жалпы айтқанда кез-келген бірақта регистр-
модификатор болса жақсы, яғни ВХ, ВР, SІ немесе DІ, өйткені процедураға осы
регистр бойынша модификациялауға тура келеді.
Біздің D процедура үшін біз ВХ регистрін таңдайық. Онда ол оның
орындалуға басталғанда ВХ регситрінде біз мазмұнын өзгертуіміз керек ұяшық
адресі болатынын білдіреді. Мұндай жағдайда бұл ұяшыққа дейін (ВХ)
конструкция көмегімен жетуге болады.
Айтылғанның барлығын ескере келе D(А) және D(В)-жолдауларға сәйкес
келетін негізгі бағдарламаның келесі фрагментін және келесі D процедурасын
аламыз: ( PUSH және POP командаларына көңіл аудармай тұра тұрыңыз)
;негізгі бағдарлама ;процедура: ВХ=адресі,
. . . x:=x dіv 16
LEA BX, A ;BX:=A адресі D PROC
CALL D ;D(A) PUSH CX ;CX-ті құтқару
LEA BX, B ;BX=B адресі MOV CL, 4
CALL D ;D(B) SHR WORD PTR [BX], CL;
. . . X:=X DІV 16
POP CX ;CX–ті қайта құру
RET
D ENDP
9.3.3. Процедурада регистрлерді сақтау.
Көріп отырғандарыңыздай, біздің D процедурамызға жылжыту мәнін жазатын
СL регистрі керек болды. Процедураның осы регистрді өзгертуге, бұзуға құны
бар ма деген сұрақ туындайды.
Бұл өте маңызды проблема. Себебі ПК-да регистрлер саны сондай көп емес
және әрбір командада дерлік қандайда бір регистр қолданылады. Сондықтан
негізгі бағдарлама мен процедураға үлкен ықтималдықпен жұмыс істеуге бірдей
регистр қажетті болуы мүмкін және осымен олар бір-біріне кедергі жасайды,
Әрине, негізгі бағдарламамен процедура өзгеше регистрлер қолданғандай
келісуге болады, бірақта оны істеу қиын, себебі ПК-да регистрлер өте аз.
Сондықтан көбінесе өзгеше жасайды: негізгі бағдарламағада, процедураға да
бір регистрмен жұмыс істеуге рұқсат берілді, бірақта процедурадан регистрге
негізгі бағдарлама жазған мәндерді еске сақтауын талап етеді. Оған жету
оңай: өз жұмысының басында процедура стекта оған жұмыс кезінде керек
болатын регистрлер мәндерін құтқарып, бұл регистрлерді өз қалауынша
қолданып және шығардың алдында ол бұл регистрлердің мәндерін стектан оқып
қайта құру керек. Дәл осылай біз D процедурасында жасадық. Шындығында,
мұнда бір құлық бар: бізге а байттық регистр мәнін сақтау керек, ал біз
білетініміздей стекқа тек сөз өлшемдігі ғана жазуға болады. Сондықтан
стекта с регистрін емес, ал толық СХ регистрін құтқарып жұмыс соңында толық
сх регитрін қайта қалпына келтіреміз.
Мұндай сақтауларды кез-келген процедурада негізгі бағдарламамен
процедура регистр қолданбағаны нақты көрініп тұрса да жасалуы ұсынылады.
Себебі бағдарламаның бастапқы мәтіні өзгеріп (ал ол өте жиі болады) бұл
өзгерістерден кейін негізгі бағдарламаға осы регистрлер керек болып қалуы
мүмкін және егер біз процедураны дұрыстап теруді еске түсірсек жақсы.
Себебі көп жағдайларда бұл туралы ұмытылып кетеді және негізгі
бағдарламамен процедура бір-біріне кедергі жасай бастайды. Сондықтан
процедурада регистрлерді сақтауды алдын ала ойласақ негізгі бағдарламаның
кез-келген өзгерісінде біз регистрлер жайлы уайымдамаймыз.
Процедура нәтижені қайтаратын регистр мәнін әрине сақтап керегі жоқ.
Себебі процедураның жұмысының мәні осы регистрдің өзгерісінде жатыр.
9.3.4. Қиын түрдегі параметрлерді беру.
Енді параметр болып күрделі түрде берілгенде параметрді жөнелтулер
бойынша беруді қарастырайық. Процедура бұл берілгенде ауыстырмаса да оған
көбінесе берілгеннің өзі емес ал оның бастапқы адресі беріледі. Бұл адресті
біле, процедура осы берілгеннің кез-келген жеріне жете алады келесі мысалды
қарастырайық. Таңбасыз сандар массивтері болсын:
X DB 100 DUP(?)
Y DB 25 DUP (?)
және DL регистрінеосы массивтердің максимал элементтерінің қосындысын
жазу керек:
DL=max(x[і])+max(y[і])
Мұнда максимал элементті екі рет табу керек болғандықтан бұл әрекетті
қайталамас үшін процедура түрінде сипаттаған мәнді болады. Оны MAX деп атап
процедураға массивтің бастапқы адресі ВХ регистрі арқылы, массивтегі
элементтер саны СХ- регистр арқылы беріледі, ал процедура өз нәтижесін AL
регистрі арқылы қайтарады деген шартпен сипаттаймыз. Бұл келісімдерге
процедура сипаттамасы және қойылған есепті шешетін негізгі бағдарлама
фрагменті келесі көріністе болады:
;MAX процедурасы: AL:=max(w[0..N-1]),
мұндағы ВХ= W-ның баст. адресі, СХ=N.
MAX PROC
PUSH BX ; процедурада қолданылатын регистрлерді
;құтқару.
PUSH CX
MOV AL, 0 ; AL=0 (максимумның баст. мәні)
MAX1: CMP[BX], AL
JLE MAX2 ;W[і]AL(AL:=W[і]
MOV AL, [BX]
MAX2: ІNC BX
LOOP MAX1
POP CX ;регистрлерді қайта құру.
POP BX
RET ;процедурадан шығару
MAX ENDP
. . .
DL=max(X[і])+max(Y[і]) есептеу үшін негізгі бағдарлама фрагменті.
LEA BX, X ;BX=X массивтің баст. адресі
MOV CX, 100 ;CX=X массивінде элемент саны
CALL MAX ;AL=MAX(X[і])
MOV DL, AL ;AL-ді құтқару
LEA BX, 4 ;BX=4 массивтің баст. адресі
MOV CX, 25 ;CX=4 массивінде элемент саны
CALL MAX ;AL=MAX(Y[і])
ADD DL, AL
. . .
9.4. Стек арқылы параметрлерді беру.
Регистр арқылы параметрлер беру – ыңғайлы және жиі қолданылады.
Бірақта былай тек параметрлер аз болғанда ғана жасауға болады: ал егерде
параметрлер көп болса оларға регистр жетпейді. Мұндай жағдайда
параметрлерді стек арқылы беру тәсілі қолданылады: негізгі бағдарлама
берілген параметрлерді (олардың мәндері немесе адрестері) стекқа жазады, ал
процедура одан кейін оларды одан алады. Бұл ойды нақты әртүрлі жолдармен
жүзеге асыруға болады. Біз параметрлерді стек арқылы берудің Турбо Паскаль
тілінің трансляторы қолданатын тәсілін қарастырамыз.
Р процедура к параметрге ие болсын: Р(а1, а2, к). Процедураға
жолданбас бұрын негізгі бағдарлама параметрлерді стекқа жазады деп
келісейік. Ал қандай ретте екенін бағдарлама авторы өзі біледі. Біз стекқа
параметрлер солдан оңға қарай жазылады деп есептейміз: алдымен 1-ші, содан
кейін 2-ші же т.с.с. Егер анықтық үшін әрбір параметр сөз өлшемді және оны
есептеу керегі жоқ деп келіссек, онда процедураға жолдау жасайдың негізгі
бағдарлама командалары келесі болады (оң жақта бұл командалар орындалғаннан
кейінгі стек жағдайы көрсетілген, яғни процедураға кіре берісте).
;P(a1, a2,..., ak) жолдау
PUSH a1 SP(
PUSH a1
. . .
PUSH ak
CALL P
(AB) . . .
Енді процедура жұмыс істей бастайды. Және мұнда бір проблема пайда
болады оған параметрге қалай жету? Бұл стек элементіне оны стектан оқымай
жету проблемасы. Ол қалай шешілетінін біз білеміз: ВР регистрін қолдану
керек яғни оған стек шынының адресін жөнелтіп (SP регистрінің мазмұнын).
Содан кейін процедура параметрлеріне жетуге [BP+і] түрдегі көріністі
қолдану керек. Бірақта мұнда бір бірақта бар: бір ВР регистрін бұзып
жатырмыз, ал ол негізгі бағдарламада қолданылуы мүмкін. Сондықтан алдымен
осы регистрдің әуелгі мәнін сақтап тек содан кейін ғана оған SP регистрінің
мәнін жөнелту керек. Осылайша, процедураның орындалуы келесі процедураның
"кіретін" әрекеттері деп аталатын командалардан басталуы керек. (оң жақта
осы командалардан кейінгі стек жағдайы көрсетілген; анықтық үшін процедура
жақын және сондықтан қайтару адоресі стекта тек бір сөз алады деп
санаймыз);
; процедураның "кіретін" әрекеттері
P PROC SP, BP(
PUSH BP ;BP-ны құтқару +2
MOV BP, SP ;BP-ны стек шынына +4
. . .
процедура командалары
Суреттен көріп отырғандарыңыздай стекқа ВР-ның (ВРст) ескі мәнін
жазғаннан кейін процедураның параметріне жету үшін келесі көріністер
қолданылады:
[ВР+4]-соңғы параметрге жету үшін; [BP+6]-соңғының алдындағы параметрге
жету үшін же т.б. Мысалға: АХ регистріне (АХ:=ак) соңғы параметрді оқу MOV
AX, [BP+4] командасымен жасалады.
Әрі қарай процедура командалары келеді. Олар біткеннен кейін
процедура "кіретін" деп аталатын әрекеттерді жасауға міндетті. Оларды
қарастырайық. Алдымен осы сәтке стек "кіретін" әрекеттерден кейінгі
жағдаймен бірдей болуы керек екенін айтып кетейік. (Егер ол олай болмаса,
онда бұл жағдайды MOV SP, BP командасымен қайта құруға болады). Сонда
процедура жұмысының соңына қарай стек шынында ВР регистрінің ескі мәні
болады. Оны оқып ВР-ны қайта құрамыз. Енді стек шынында қайтару адресі
болады. RET командасын орындап, процедурадан шығуға болатындай, бірақта ол
олай емес-әлі стекті керек емес параметрден тазарту керек. Бұл тазартуды
кім істеу керек? Процедура ма? Әлде негізгі бағдарлама ма? Әрине, оны
негізгі бағдарлама жасай алады, ол үшін сонда CALL P командасынан кейін ADD
SP, 2*k командасын орындау керек. Бірақта егер стекті тазартуды
процедураның өзі жасаса жақсы. Өйткені процедураға жолдаулар көп, сондықтан
негізгі бағдарламада ADD командасын бірнеше рет жазуға тура келеді, ал
процедура біреу, сондықтан оның ішінде мұндай команданы тек бір рет жащу
керек. Бұл жалпы ереже: егер қандайда бір нәрсені негізгі бағдарлама да,
процедура да жасай алса, онда оны процедураның жасағаны жақсы. Себебі
мұндайда аз команда болады.
Сонымен, процедура алдымен стекті параметрлерден босатып және тек
содан кейін басқаруды қайтару адресі бойынша беруі тиіс. Бұл жұп әрекеттің
жасалуын ықшамдау үшін ПК командалар жүйесіне RET командасының ұлғайтылған
варианты енгізілген. Ол таңбасыз сан ретінде қарастырылатын тікелей
операндпен:
RET 116
Бұл команда бойынша алдымен стектан қайтару адресі алынады, содан
кейін стек операнд арқылы көрсетілген байтқа тазартылады және әрі қарай
қайтару адресі бойынша көшу орындалады:
Стек ( ІP, [стек(CS] SP:=SP+116
("стек(CS" әрекеті тек алыс қайтару да ғана орындалады).
Бірнеше ескертулер жасайық. Біріншіден RET командасы – іс жүзінде RET
0 командасы, яғни стекті тазартусыз қайтару. Екіншіден команданың операнды
стектің қанша сөзге емес, ал қанша байтқа тазарту керек екенін көрсетеді,
сондықтан стекті әрбірі сөз өлшемді параметрден тазарту үшін к емес ал 2*к
операндын көрсету керек. Үшіншіден операнда қайтару адресі ескерілмеуі тиіс
- RET командасы оны стекті тазартқанға дейін оқиды.
Айтылғанның барлығын ескере келе процедураның "шығатын" әрекеттері
мынадай:
;процедураның "шығатын" әрекеттері
POP BP ;BP-ның ескі мәнін қайта құру.
RET 2*k ;стекті к параметр сөзден тазарту және қайтару.
P ENDP
Процедурадан мұндай қайтарудан кейін стек жағдайы процедураға жолдау
командасы жасалғанға дейінгі жағдаймен бірдей болады, яғни стекқа
параметрлерді жазу командалары орындалғанға дейін. Осылайша стекта барлық
процедураға жолдаулар жойылады, ал бұл нағыз бізге керегі.
Стек арқылы параметр берудің жалпы құрылысы осындай. Мұндай
параметрлерді беру тәсілі универсал екенін тағы да айтып кетейік. Оны кез-
келген параметр санында қолдануға болады. Бірақта бұл тәсіл регистр арқылы
беруден қиындау, сондықтан егер мүмкін болса параметрлерді регистр арқылы
берген үнемді. Бұл тәсіл оңай және қысқа. Ал процедура нәтижесіне келетін
болсақ, ол стек арқылы өте сирек беріледі, көбінесе регистр арқылы
беріледі.
Нақты мысал ретінде А адресінен бастап бірінші параметр (А) адресі
және екінші параметр (N) мәні стек арқылы берілетін шартымен зерденің N
байтын нольдейтін NULL(A,N) процедурасын сипаттаймыз. Процедура сипаттамасы
оң жақта келтірілген, ал сол жақта 100-байтты х массиві нольденетін
NULL(Х,100) жолдауды жасалуы көрсетілген.
X DB 100 DUP(?)
. . .
; NULL(X,100) шақыру
LEA AX, X
PUSH AX ;х адр. (стек
MOV AX, 100
PUSH AX ;100(стек
CALL NULL
. . .
NULL PROC
PUSH BP ;"кіретін" әрекеттер
MOV BP, SP
PUSH BX ;ВХ және СХ-ті құтқару
PUSH CX ;(ВРст "үстінде")
MOV CX, [BP+4] ;CX=A
MOV BX, [BP+6] ;BX=N
NULL1: MOV BYTE PTR[BX], 0 ;нольдеу
ІNC BX ;А(0, . . N-1)
LOOP NULL1
POP CX ;СХ және ВХ-ті қайта құру.
POP BX
POP BP ;"шығатын" әрекеттер
RET 4
NULL ENDP
9.5. Процедуралардың жергілікті берілгендері.
Көптеген процедураларда жергілікті берілгендері сақтаумен қиындық
тумайды – олар үшін регистрлерде жергілікті. Бірақта егер процедурада
жергілікті берілгендер көп болса, оларға орында қай жерден бөлу керек деген
сұрақ туындайды. Әрине, берілгендер сегментінде бөлуге болады, бірақта ол
жаман, өйткені уақыттың көп бөлігін зерденің бұл орны жай өседі. Бұл орынды
стекта бөлген жақсы: процедураға кірерде стек шынында жергілікті
берілгендерге керекті байт саны алынып, ал шығар алдында бұл орын
босатылады. Мұндай жағдайда зердедегі орын тек процедура орындалу уақытына
ғана алынады.
Стекта мұндай орынды "алуды" параметрлерді регистр арқылы да, стек
арқылы да процедураға бергенде жасауға болады. ол үшін стекта ВР
регистрінің дәл қазіргі мәнін еске сақтап оны стек шынына орнату керек.
Осыдан кейін SP стек көрсеткішінің мәнін "алатын" байт санына азайту керек.
Мысалға, егер қандайда Р процедурасына 3 байт керек болса (айталық 2 байт А
жергілікті айнымалы үшін және 1 байт В жергілікті айнымалыға), сонда
көрсетілген әрекеттер келесі командалармен жасалады.
P PROC SP( -3 жергілікті
PUSH BP -2 берілгендер
MOV BP, SP
SUB SP, 3
. . .
Осыдан кейін жергілікті берілгендерге жету [BP-k] түріндегі көрініс
көмегімен жүзеге асады; мысалға [BP-2] –бұл А жергілікті айнымалы адресі,
ал [BP-3] –В жергілікті айнымалының адресі.
Процедура жұмысын аяқтағанда мынадай әрекеттерді жасау керек:
. . .
MOV SP, ... жалғасы
9.2.1.Подпрограмманы қай жерге орналастыру керек. 1
9.2.2. Подпрограмманы қалай безендіреді 2
9.2.3.Процедураларды шақыру және олардан қайту. 3
9.2.4. CALL командасының басқа варианттары. 7
9.3. Регистр арқылы параметр беру. 8
9.3.1. Мәндер бойынша параметрлерді беру. 9
9.3.2. Жөнелту арқылы параметрлер беру. 9
9.3.3. Процедурада регистрлерді сақтау. 11
9.3.4. Қиын түрдегі параметрлерді беру. 12
9.4. Стек арқылы параметрлерді беру. 13
9.5. Процедуралардың жергілікті берілгендері. 18
9.6. Рекурсивті процедуралар. 21
10 ТАРАУ. Берілгендердің динамикалық құрылымдары. 25
10.1. Жолдық командалар. 25
10.1.1. Жолдарды салыстыру командасы. 26
10.1.2. Қайталау префикстері. 29
10.1.3. Басқа жолдық командалар. 34
9.2.1.Подпрограмманы қай жерге орналастыру керек.
Бірінші проблема – ол қай жерге подпрограмманы орналастырамыз? Жалпы
айтканда кез-келген жерде. Бірақта сонымен қатар подпрограмма өзінен өзі
орындалмауы керек, ал тек оған жолдағанда ғана орындалуы тиіс екенін түсіну
керек. Соңдықтан оларды байқаусыздан басқару оларға көшпегендей орналастыру
керек. Егер бірнеше подпрограмма болса, әдетте оларды қатар орналастырады.
Әдетте подпрограмманы (nn) не FІNІSH командасынан кейін командалар
сегментінің соңына (қар а сурет) не бағдарлама орындала басталатын
команданың алдында, яғни осы сегментінің ең басында (б-суреті). Үлкен
бағдарламаларда подрпаграммаларды көбінесе бөлек командалар сегметінде
орналастырады (в-суретің қар)
С SEGMENT С SEGMENT С1SEGMENT
BEG: ...
FІNІSH
BEG: ... C1 ENDS
С SEGMENT
C ENDS C ENDS BEG: ...
END BEG END BEG ... .
a) б) C ENDS
END BEG
в)
9.2.2. Подпрограмманы қалай безендіреді
Екінші проблема ол подпрограмманы қалай сипаттау керек? Жалпы
жағдайда подпрограмма құрайтын командалар тобын бағдарлама тексінде ешқалай
белгілемеуге болады. Бірақта ЯА-да подграммаларды әдейі безендіру
бекітілген. Ол процедуалар түрінде. Ол белгілі бір пайда келтіреді. Бірақта
ол туралы кейіңірек. Алдағы уакытта біз тек процедура ретінде сипатталған
подрограммаларды қолданамыз.
Подпрограммаларды процедура түрінде сипаттау былай болады:
процедура есіміPROCпараметр
процедура денесі
процедура есіміENDP.
Көріп отырғандарыңыздай процедура денесінің оның командаларының
алдында РRОС procedure директивасы қойылады, ал одан кейін -ENDP
директивасы end of procedure. Бұл қос директивада да бір есім
көрсетіледі. Ол есім болып табылады. РRОС директивасында есімнен кейін қос
нүкте қойылмайтынына көңіл бөлу керек, бірақта ол есім белгі болып
саналады. Ол процедуранын бірінші командасына бағытталған деп саналады.
Мысалға, процедура есімін көшу командасында көрсетуге болады, онда
процедураның бірінші командасына көшу жасалады.
РRОС директивасында параметр болмауы да мүмкін, онда ол NEAR-ға тең
деп саналады. осыған байланысты көбінесе NEAR параметрі көрсетілмейді
NEAR параметрінде не параметр жоқ болғанда процедура жақын, ал FAR
параметрінде –алыс болады. Жақын процедураға тек ол сипатталған командалар
сегментінен жолдауға болады. Ал басқа сегменттерден болмайды, ал алыс
процедураларға кез-келген сегменттен жолдануға болады. Жақын және алыс
процедураларының айырмашылығы тек осында.
ЯА-да процедурада сипатталған есімдер мен белгілер оның ішінде
жерлігіктенбейді, сондықтан олар уникальды болуы тиіс, яғни басқа
есімдермен сәйкес келмеуі тиіс. ЯА-да бір процедураны екінші процедура
ішінде сипаттауға болса да, ол ешқандай пайда бермейді, сондықтан ЯА-да
процедураларды бір-біріне салу қолданылмайды.
9.2.3.Процедураларды шақыру және олардан қайту.
Келесі проблема – ол процедураларды қалай шақыру және олардан қалай
қайту? Жоғары деңгейдегі тілге программалағанда процедураны жұмысқа қосу
үшін тек оның есімін және параметрлерін жазу жеткілікті. Сонымен қатар
процедура жұмысын қалай бастайды, басқаруды негізгі бағдарламаға қалай
қайтаратыны бізді ойландырмайды: осының барлығы үшін транслятор жауап
береді. Ал егер біз ЯА-да бағдарлама жасасақ, онда негізгі бағдарламамен
процедуралар арасындағы көшулерді өзімізге жасуға тура келеді. Ол қалай
жасалатынын қарастырайық.
Мұнда екі проблема: негізгі бағдарламадан процедураны жұмыс істетуге
болады және процедурадан негізгі бағдарламаға қалай қайту. Бірінші проблема
қарапайым түрде шешіледі: процедураның бірінші командасына көшу жасасақ
жеткілікті, яғни көшу командасында процедура есімін көрсетсек жеткілікті.
Екінші проблема қиындау. Себебі процедураға негізгі бағдарламаның кез-
келген жерінен жолдауға болады, және сондықтан процедурадан әртүрлі жерге
қайту керек. Процедураның өзі басқаруды қай жерге қайтару керек екенін
білмейді, бірақта оны негізгі бағдарлама біледі. Сондықтан процедураға
жолдағанда негізгі бағдарлама міндетті түрде қайтатын адресті – процедура
өз жұмысын бітіргеннен кейін көшу жасауға міндетті негізгі бағдарлама
командасының адресін хабардар ету керек. Әдетте бұл процедураға жолдау
командасынан кейінгі команда. Нақты осы адресті негізгі бағдарлама
процедураға хабарлайды, нақты осы адрес бойынша процедура негізгі
бағдарламаға қайтады.
Процедураға әртүрлі жерден жолдау жасағандықтан және қайту адресі
әртүрлі көрсеткендіктен, басқаруды негізгі бағдарламаның әртүрлі жерінен
қайтарады.
Қайтару адресін қалай хабардар етеді? Оны әртүрлі жолдармен жасауға
болады. Біріншіден, регистр арқылы беріп жіберуге болады: негізгі
бағдарлама қандайда бір регистрге қайтару адресін жазады, ал процедура оны
сол регистрден алып сол бойынша көшу жасайды. Екіншіден, стек арқылы
жасауға болады: процедураға жолданбастан бұрын негізгі бағдарлама қайтару
адресін стекқа жазады, ал процедура оны содан алып сол бойынша көшу
жасайды. ПК-да қайтару адресі стек арқылы беру бекітілген, сондықтан алдағы
уақытта біз қайтару адресін берудің тек осы жолын қарастырамыз.
Қайтару адресін стек арқылы беру және осы адрес бойынша қайтаруды біз
білетін командалар көмегімен жүзеге асыруға болады. бірақта реал
бағдарламаларда процедуралар өте жиі қолданылады, сондықтан ПК командалар
жүйесіне негізгі бағдарлама мен процедуралар арасындағы көшулерді жасауды
оңайлататын әдейі командалар қосылған. Олар CALL және RET командалары. Бұл
командалардың негізгі варианттары келесі:
процедураны шақыру (қайтарымды көшу): CALLпроцедура
процедурадан қайту (return):RET
CALL командасы стекқа өзінен кейін келетін команданы жазады және содан
кейін келетін команданы жазады және содан кейін көрсетілген процедураның
бірінші командасына көшу жасайды. RET командасы стек шынынан адресті оқып
көшуді сол бойынша жасайды.
Келесі мысалда қарастырайық. Біз өз бағдарламамызды реттелік делік
және ол үшін оның әртүрлі жеріне х және у бірдей айнымалылардың реттеуші
жазуларын қоямыз. Мұндай жазулар бірнеше командамен жүзеге асады, олар
бірнеше рет қайталанады, сондықтан бұл жазуды процедура ретінде сипаттап
негізгі бағдарламада оған тек қысқа жолдаулар көрсету мәнді болады:
;негізгі бағдарлама ;процедура
. . . PR PROC
CALL PR OUTІNT X
(a) . . . . OUTІNT Y,8
. . . . NEWLІNE
CALL PR RET
(b) . . . . PR ENDP
Бірінші CALL командасы орындалғанда ол өзінен кейін келетін команданың
адресін стекқа жазып басқаруды PR процедураның басына - OUTІNT X
командасына береді. Процедура жұмыс істей бастайды: ол Х және У-ті жазып
жолды тасымалдайды. Содан кейін RET командасы стектағы адресін алып сол
бойынша көшу жасайды. Осылайша бірінші CALL командасынан кейін келетін
командадан негізгі бағдарлама жұмысы қайта басталады. Процедураға кезекті
жолдау осылайша жасалады, бірақта екінші CALL командасы бойынша стекқа
басқа адрес – в адресі жазылады, сондықтан процедура осы жолы басқаруды
негізгі бағдарламаның басқа жеріне – екінші CALL командасынан кейін келетін
командаға.
Енді CALL және RET командаларға қатысты кейбір нақтылаулар жасайық.
ПК-да келесі болып орындалатын команда CS және ІP регистлерімен
берілейтінің айтып кетейік. Егер процедура сипаттамасыз біз оған жолданатын
командалар сегментінде орналасса, онда оған көшу және одан қайту жақын
болуы тиіс, яғни тек ІP команда көрсеткіші ғана өзгеруі тиіс. Өйткені біз
әрдайым бір командалар сегментінде қаламыз. Ал егер процедура басқа
командалар сегментінде орналасса, онда оған көшу және одан қайту алыс болуы
тиіс, яғни қос регистрде өзгеруі тиіс.
Осының барлығы ескеріледі және іс жүзінде ПК-да CALL және RET
машиналық командалардың екі-екі варианттары бар. Егер АВ арқылы қайту
адресі – CALL командасынан кейін келетін команда адресін белгілесек, онда
CALL Р командасының, мұндағы Р-процедура есімі, қос вариантында былай
сипаттауға болады:
жақын қайтару: стек ( ІP
алыс қайтару: стек( ІP, стек CS.
CALL және RET командалары келісімді жасалуы тиіс екені түсінікті:
процедураға шақыру жақын болғанда одан қайту да жақын болуы тиіс, ал шақыру
алыс болғанда қайту да алыс болуы тиіс, кері жағдайда бағдарлама дұрыс
жұмыс істемейді. Сонымен қатар ЯА-да әрбір команданың қос варианттары
бірдей жазылады әрине, онда ЯА-да негізгі бағдарлама мен процедура арасында
көшу түрлері қалай көрсетіледі екен деген сұрақ туындайды? Жауабы мындай:
ол нақты көрсетілмейді, бұл проблеманы біз үшін ассемблер шешеді. Егер біз
процедураны жақын ретінде сипаттасақ онда осы процедура есімі көрсетілген
барлық CALL командаларын ассемблер жақын шақыру машиналық командаларына
жөнелтеді, ал барлық осы процедураның ішінде орналасқан RET командаларын
ассемблер жақын қайтару машиналық командаларға жөнелтеді. Егер процедура
алыс болып сипатталса, онда барлық оған жолдаулар алыс шақыру ретінде ал
оның ішіндегі барлық RET командалары – алыс қайтарулар ретінде жөнелтіледі
(процедуралар сыртында RET жақын қайтару ретінде қарастырылады). Осылайша
CALL және RET командалар арасындағы сәйкестікті қарауды ассемблер өзіне
алады, ал біздің оған басымыз ауырмауы тиіс. Подпрограммаларды процедуралар
түрінде жазудың нақты пайдасы осында, нақты осы себепті ЯА-да
подпрограммаларды процедура ретінде сипаттау бекітілген.
Бірақта мұнда бір құлық бар. Ассемблер CALL командасы үшін дұрыс
вариантты тек егер процедура осы командаға дейін сипатталса ғана таңдайды.
Егер процедура кейін сипатталатын болса, онда ассемблер бұл процедураның
түрін (жақын не алыс) білмей CALL командасын кездестіргенде оны жақын деп
санап (көбінесе солай болады) әрқашан жақын шақырудың машиналық командасын
жасайды. Ал егер одан кейін ассемблер берілген процедура алыс екенін біосе,
қате деп табады. Мұндай қателік жібермес үшін CALL командасында PTR
операторы көмегімен процедура алыс екенін нақты көрсету керек:
CALL ҒАR PTR Р
9.2.4. CALL командасының басқа варианттары.
Біз CALL командасының негізгі варианттары – оның операнды ретінде
процедура есімі көрсетілетін жағдайды қарастырдық. Бірақта операндтың JMP
шартсыз көшу командасындағыдай SHORT операторымен жағдайдан басқа, басқа да
варианттары мүмкін.
Мысалдар:
NA DW P
FA DO Q
. . .
P PROC
. . .
P1: . . .
. . .
P ENDP
Q PROC FAR
. . .
Q1: . . .
. . .
Q ENDP
. . .
CALL P1; P1-ге
CALL FAR PTR Q1; Q1-ге қайтымды алыс көшу
CALL Q1; Q1-ге қайтымды жақын (!) көшу
CALL NA; P процедурасын жақын шақыру
CALL FA; Q процедурасын алыс шақыру
LEA BX, Q
CALL [BX]; Q процедурасын жақын (!) шақыру
CALL QWORD PTR [BX]; Q процедурасын алыс шақыру
CALL командасының бұл варианттарын қолданғанда абайлық сақтау керек.
Біріншіден, қайтарымды көшу көшу түрінің RET командасы бойынша қайтару
түрімен келісімділігін байқау керек, өйткені бұл жағдайларда ассемблер
мұндай келісімділікке жауап бермейді. Екіншіден JMP командасындағыдай CALL
командасында алға жөнелту немесе жанама жөнелтулерде егер керек болса
жөнелту түрлерін нақтылау керек (үндемеушіліктен алға жөнелтуде ассемблер
тура шақыруды шығарады, ал жанама жөнелтуде жақын жанама шақыруды).
9.3. Регистр арқылы параметр беру.
Енді процедура параметрлерімен байланысты проблемаларды қарастырайық.
Жоғары деңгейдегі тілдерде процедуралар үшін параметрлерді беру үшін
оларды процедураларды шақыру операторында жазу жеткілікті. ЯА-да параметр
беру проблемасы жеңіл емес, сондықтан біз оны нақтырақ қарастырамыз.
Сонымен қатар процедура нәтижесі қалай қайтарылатынын қарастырамыз. ЯА-
да процедура мен функцияға формалды бөлулер жоқ, екеуінде процедуралар деп
аталады. Бірақта оларға қатысты оларды функцияға және таза процедураларға
бөлуге болады. олар нәтиже шығарма жақ екеніне байланысты.
Процедураларға параметрлерді әртүрлі жолдармен беруге болады. Ең
қарапайым тәсіл – ол параметрлерді регистр арқылы беру: негізгі бағдарлама
параметрлерді қандайда регистрлерге жазады, ал процедуралар одан кейін оны
өз жұмысында регистрден алып қолданады. Осылайша егер нәтиже болса, жасауға
болады: процедура өз нәтижесін бір регистрге жазады ал негізгі бағдарлама
содан кейін регистрден нәтижені алады. Параметрлермен нәтижелерді қандай
регистр арқылы беру ол бағдарлама авторының өз қалауы регистрлерді өзі
анықтайды.
9.3.1. Мәндер бойынша параметрлерді беру.
Мындай мысал қарастырайық. c=max(a,b)+max(5, a-1)-ны есептеу керек,
мұндағы барлық сандар таңбалы және сөз өлшемді. max(х,у)-ті есептеуді
процедура-функция ретінде сипаттаймыз, сонымен қатар келесі жайлы
келісейік: негізгі бағдарлама бірінші параметрді (х) АХ регистрі арқылы
беру керек, екінші параметрді (у) ВХ регистрі, ал нәтижені (max) процедура
АХ регистрі арқылы қайтаруы тиіс. Бұл шарттарда процедура және негізгі
бағдарламаның фрагменті былай көрінеді.
;процедура: АХ=max(АХ,ВХ) ;негізгі программа
MAX PROC FAR . . .
CMP AX, BX MOV AX, A ;AX:=a
JGE MAX1 MAX BX, B ;BX:=b
MOV AX, BX CALL MAX ;AX:=max(a,b)
MAX1: RET MOV C, AX ;AX-ті құтқару
MAX ENDP MOV AX, S ;AX:=S
MOV BX, A
DEC BX ;BX:=a-1
CALL MAX ;AX:=MAX(5, a-1)
ADD C, AX ;C:=MAX(a,b)+MAX(5, a-1)
Егер Паскаль тілінің терминологиясын пайдалансақ, онда бұл мысалда
параметрлер мәндер арқылы беріледі: процедураға жолданбастан бұрын негізгі
процедура параметр мәндерін есептеп осы мәндерді регистрлерге жазады. Ал
енді параметрлерді берудің басқа тәсілі – жөнелту арқылы тәсілін
қарастырайық.
9.3.2. Жөнелту арқылы параметрлер беру.
Паскаль тілінде келесі процедураны алайық:
Produce D(var x:іnteger); begіn x:=xdіv 16 end;
Программада оларға мындай жолдау болсын: D(А) және D(В), мұндағы А
және В – мәндері теріс емес сандар болып табылатын айнымалылар есімі.
Көріп отырғандарыңыздай процедура өз параметрлеріне бірдеңе атайды.
Машиналық тіл терминдерінде атау зерденің қандайда бір ұяшығына жазуды
білдіреді, ал ұяшыққа бірдеңе жазу үшін бұл ұяшықтың адресін (есімін) білу
керек. Сондықтан процедура жазу жазуы керек ұяшықтың (А не В) адресін білу
керек, ал оны негізгі бағдарлама хабардар етуге міндетті Осылайша
параметрлерді жөнелту арқылы беру сәйкес параметрге ұяшық адресін (есімін)
беруді білдіреді.
Адресті қалай беру керек? Регистр арқылы: негізгі бағдарлама қандайда
бір регистрге айнымалының адресін жазады, ал процедура оны сол регистрден
алады. Ол қандай регистр? Жалпы айтқанда кез-келген бірақта регистр-
модификатор болса жақсы, яғни ВХ, ВР, SІ немесе DІ, өйткені процедураға осы
регистр бойынша модификациялауға тура келеді.
Біздің D процедура үшін біз ВХ регистрін таңдайық. Онда ол оның
орындалуға басталғанда ВХ регситрінде біз мазмұнын өзгертуіміз керек ұяшық
адресі болатынын білдіреді. Мұндай жағдайда бұл ұяшыққа дейін (ВХ)
конструкция көмегімен жетуге болады.
Айтылғанның барлығын ескере келе D(А) және D(В)-жолдауларға сәйкес
келетін негізгі бағдарламаның келесі фрагментін және келесі D процедурасын
аламыз: ( PUSH және POP командаларына көңіл аудармай тұра тұрыңыз)
;негізгі бағдарлама ;процедура: ВХ=адресі,
. . . x:=x dіv 16
LEA BX, A ;BX:=A адресі D PROC
CALL D ;D(A) PUSH CX ;CX-ті құтқару
LEA BX, B ;BX=B адресі MOV CL, 4
CALL D ;D(B) SHR WORD PTR [BX], CL;
. . . X:=X DІV 16
POP CX ;CX–ті қайта құру
RET
D ENDP
9.3.3. Процедурада регистрлерді сақтау.
Көріп отырғандарыңыздай, біздің D процедурамызға жылжыту мәнін жазатын
СL регистрі керек болды. Процедураның осы регистрді өзгертуге, бұзуға құны
бар ма деген сұрақ туындайды.
Бұл өте маңызды проблема. Себебі ПК-да регистрлер саны сондай көп емес
және әрбір командада дерлік қандайда бір регистр қолданылады. Сондықтан
негізгі бағдарлама мен процедураға үлкен ықтималдықпен жұмыс істеуге бірдей
регистр қажетті болуы мүмкін және осымен олар бір-біріне кедергі жасайды,
Әрине, негізгі бағдарламамен процедура өзгеше регистрлер қолданғандай
келісуге болады, бірақта оны істеу қиын, себебі ПК-да регистрлер өте аз.
Сондықтан көбінесе өзгеше жасайды: негізгі бағдарламағада, процедураға да
бір регистрмен жұмыс істеуге рұқсат берілді, бірақта процедурадан регистрге
негізгі бағдарлама жазған мәндерді еске сақтауын талап етеді. Оған жету
оңай: өз жұмысының басында процедура стекта оған жұмыс кезінде керек
болатын регистрлер мәндерін құтқарып, бұл регистрлерді өз қалауынша
қолданып және шығардың алдында ол бұл регистрлердің мәндерін стектан оқып
қайта құру керек. Дәл осылай біз D процедурасында жасадық. Шындығында,
мұнда бір құлық бар: бізге а байттық регистр мәнін сақтау керек, ал біз
білетініміздей стекқа тек сөз өлшемдігі ғана жазуға болады. Сондықтан
стекта с регистрін емес, ал толық СХ регистрін құтқарып жұмыс соңында толық
сх регитрін қайта қалпына келтіреміз.
Мұндай сақтауларды кез-келген процедурада негізгі бағдарламамен
процедура регистр қолданбағаны нақты көрініп тұрса да жасалуы ұсынылады.
Себебі бағдарламаның бастапқы мәтіні өзгеріп (ал ол өте жиі болады) бұл
өзгерістерден кейін негізгі бағдарламаға осы регистрлер керек болып қалуы
мүмкін және егер біз процедураны дұрыстап теруді еске түсірсек жақсы.
Себебі көп жағдайларда бұл туралы ұмытылып кетеді және негізгі
бағдарламамен процедура бір-біріне кедергі жасай бастайды. Сондықтан
процедурада регистрлерді сақтауды алдын ала ойласақ негізгі бағдарламаның
кез-келген өзгерісінде біз регистрлер жайлы уайымдамаймыз.
Процедура нәтижені қайтаратын регистр мәнін әрине сақтап керегі жоқ.
Себебі процедураның жұмысының мәні осы регистрдің өзгерісінде жатыр.
9.3.4. Қиын түрдегі параметрлерді беру.
Енді параметр болып күрделі түрде берілгенде параметрді жөнелтулер
бойынша беруді қарастырайық. Процедура бұл берілгенде ауыстырмаса да оған
көбінесе берілгеннің өзі емес ал оның бастапқы адресі беріледі. Бұл адресті
біле, процедура осы берілгеннің кез-келген жеріне жете алады келесі мысалды
қарастырайық. Таңбасыз сандар массивтері болсын:
X DB 100 DUP(?)
Y DB 25 DUP (?)
және DL регистрінеосы массивтердің максимал элементтерінің қосындысын
жазу керек:
DL=max(x[і])+max(y[і])
Мұнда максимал элементті екі рет табу керек болғандықтан бұл әрекетті
қайталамас үшін процедура түрінде сипаттаған мәнді болады. Оны MAX деп атап
процедураға массивтің бастапқы адресі ВХ регистрі арқылы, массивтегі
элементтер саны СХ- регистр арқылы беріледі, ал процедура өз нәтижесін AL
регистрі арқылы қайтарады деген шартпен сипаттаймыз. Бұл келісімдерге
процедура сипаттамасы және қойылған есепті шешетін негізгі бағдарлама
фрагменті келесі көріністе болады:
;MAX процедурасы: AL:=max(w[0..N-1]),
мұндағы ВХ= W-ның баст. адресі, СХ=N.
MAX PROC
PUSH BX ; процедурада қолданылатын регистрлерді
;құтқару.
PUSH CX
MOV AL, 0 ; AL=0 (максимумның баст. мәні)
MAX1: CMP[BX], AL
JLE MAX2 ;W[і]AL(AL:=W[і]
MOV AL, [BX]
MAX2: ІNC BX
LOOP MAX1
POP CX ;регистрлерді қайта құру.
POP BX
RET ;процедурадан шығару
MAX ENDP
. . .
DL=max(X[і])+max(Y[і]) есептеу үшін негізгі бағдарлама фрагменті.
LEA BX, X ;BX=X массивтің баст. адресі
MOV CX, 100 ;CX=X массивінде элемент саны
CALL MAX ;AL=MAX(X[і])
MOV DL, AL ;AL-ді құтқару
LEA BX, 4 ;BX=4 массивтің баст. адресі
MOV CX, 25 ;CX=4 массивінде элемент саны
CALL MAX ;AL=MAX(Y[і])
ADD DL, AL
. . .
9.4. Стек арқылы параметрлерді беру.
Регистр арқылы параметрлер беру – ыңғайлы және жиі қолданылады.
Бірақта былай тек параметрлер аз болғанда ғана жасауға болады: ал егерде
параметрлер көп болса оларға регистр жетпейді. Мұндай жағдайда
параметрлерді стек арқылы беру тәсілі қолданылады: негізгі бағдарлама
берілген параметрлерді (олардың мәндері немесе адрестері) стекқа жазады, ал
процедура одан кейін оларды одан алады. Бұл ойды нақты әртүрлі жолдармен
жүзеге асыруға болады. Біз параметрлерді стек арқылы берудің Турбо Паскаль
тілінің трансляторы қолданатын тәсілін қарастырамыз.
Р процедура к параметрге ие болсын: Р(а1, а2, к). Процедураға
жолданбас бұрын негізгі бағдарлама параметрлерді стекқа жазады деп
келісейік. Ал қандай ретте екенін бағдарлама авторы өзі біледі. Біз стекқа
параметрлер солдан оңға қарай жазылады деп есептейміз: алдымен 1-ші, содан
кейін 2-ші же т.с.с. Егер анықтық үшін әрбір параметр сөз өлшемді және оны
есептеу керегі жоқ деп келіссек, онда процедураға жолдау жасайдың негізгі
бағдарлама командалары келесі болады (оң жақта бұл командалар орындалғаннан
кейінгі стек жағдайы көрсетілген, яғни процедураға кіре берісте).
;P(a1, a2,..., ak) жолдау
PUSH a1 SP(
PUSH a1
. . .
PUSH ak
CALL P
(AB) . . .
Енді процедура жұмыс істей бастайды. Және мұнда бір проблема пайда
болады оған параметрге қалай жету? Бұл стек элементіне оны стектан оқымай
жету проблемасы. Ол қалай шешілетінін біз білеміз: ВР регистрін қолдану
керек яғни оған стек шынының адресін жөнелтіп (SP регистрінің мазмұнын).
Содан кейін процедура параметрлеріне жетуге [BP+і] түрдегі көріністі
қолдану керек. Бірақта мұнда бір бірақта бар: бір ВР регистрін бұзып
жатырмыз, ал ол негізгі бағдарламада қолданылуы мүмкін. Сондықтан алдымен
осы регистрдің әуелгі мәнін сақтап тек содан кейін ғана оған SP регистрінің
мәнін жөнелту керек. Осылайша, процедураның орындалуы келесі процедураның
"кіретін" әрекеттері деп аталатын командалардан басталуы керек. (оң жақта
осы командалардан кейінгі стек жағдайы көрсетілген; анықтық үшін процедура
жақын және сондықтан қайтару адоресі стекта тек бір сөз алады деп
санаймыз);
; процедураның "кіретін" әрекеттері
P PROC SP, BP(
PUSH BP ;BP-ны құтқару +2
MOV BP, SP ;BP-ны стек шынына +4
. . .
процедура командалары
Суреттен көріп отырғандарыңыздай стекқа ВР-ның (ВРст) ескі мәнін
жазғаннан кейін процедураның параметріне жету үшін келесі көріністер
қолданылады:
[ВР+4]-соңғы параметрге жету үшін; [BP+6]-соңғының алдындағы параметрге
жету үшін же т.б. Мысалға: АХ регистріне (АХ:=ак) соңғы параметрді оқу MOV
AX, [BP+4] командасымен жасалады.
Әрі қарай процедура командалары келеді. Олар біткеннен кейін
процедура "кіретін" деп аталатын әрекеттерді жасауға міндетті. Оларды
қарастырайық. Алдымен осы сәтке стек "кіретін" әрекеттерден кейінгі
жағдаймен бірдей болуы керек екенін айтып кетейік. (Егер ол олай болмаса,
онда бұл жағдайды MOV SP, BP командасымен қайта құруға болады). Сонда
процедура жұмысының соңына қарай стек шынында ВР регистрінің ескі мәні
болады. Оны оқып ВР-ны қайта құрамыз. Енді стек шынында қайтару адресі
болады. RET командасын орындап, процедурадан шығуға болатындай, бірақта ол
олай емес-әлі стекті керек емес параметрден тазарту керек. Бұл тазартуды
кім істеу керек? Процедура ма? Әлде негізгі бағдарлама ма? Әрине, оны
негізгі бағдарлама жасай алады, ол үшін сонда CALL P командасынан кейін ADD
SP, 2*k командасын орындау керек. Бірақта егер стекті тазартуды
процедураның өзі жасаса жақсы. Өйткені процедураға жолдаулар көп, сондықтан
негізгі бағдарламада ADD командасын бірнеше рет жазуға тура келеді, ал
процедура біреу, сондықтан оның ішінде мұндай команданы тек бір рет жащу
керек. Бұл жалпы ереже: егер қандайда бір нәрсені негізгі бағдарлама да,
процедура да жасай алса, онда оны процедураның жасағаны жақсы. Себебі
мұндайда аз команда болады.
Сонымен, процедура алдымен стекті параметрлерден босатып және тек
содан кейін басқаруды қайтару адресі бойынша беруі тиіс. Бұл жұп әрекеттің
жасалуын ықшамдау үшін ПК командалар жүйесіне RET командасының ұлғайтылған
варианты енгізілген. Ол таңбасыз сан ретінде қарастырылатын тікелей
операндпен:
RET 116
Бұл команда бойынша алдымен стектан қайтару адресі алынады, содан
кейін стек операнд арқылы көрсетілген байтқа тазартылады және әрі қарай
қайтару адресі бойынша көшу орындалады:
Стек ( ІP, [стек(CS] SP:=SP+116
("стек(CS" әрекеті тек алыс қайтару да ғана орындалады).
Бірнеше ескертулер жасайық. Біріншіден RET командасы – іс жүзінде RET
0 командасы, яғни стекті тазартусыз қайтару. Екіншіден команданың операнды
стектің қанша сөзге емес, ал қанша байтқа тазарту керек екенін көрсетеді,
сондықтан стекті әрбірі сөз өлшемді параметрден тазарту үшін к емес ал 2*к
операндын көрсету керек. Үшіншіден операнда қайтару адресі ескерілмеуі тиіс
- RET командасы оны стекті тазартқанға дейін оқиды.
Айтылғанның барлығын ескере келе процедураның "шығатын" әрекеттері
мынадай:
;процедураның "шығатын" әрекеттері
POP BP ;BP-ның ескі мәнін қайта құру.
RET 2*k ;стекті к параметр сөзден тазарту және қайтару.
P ENDP
Процедурадан мұндай қайтарудан кейін стек жағдайы процедураға жолдау
командасы жасалғанға дейінгі жағдаймен бірдей болады, яғни стекқа
параметрлерді жазу командалары орындалғанға дейін. Осылайша стекта барлық
процедураға жолдаулар жойылады, ал бұл нағыз бізге керегі.
Стек арқылы параметр берудің жалпы құрылысы осындай. Мұндай
параметрлерді беру тәсілі универсал екенін тағы да айтып кетейік. Оны кез-
келген параметр санында қолдануға болады. Бірақта бұл тәсіл регистр арқылы
беруден қиындау, сондықтан егер мүмкін болса параметрлерді регистр арқылы
берген үнемді. Бұл тәсіл оңай және қысқа. Ал процедура нәтижесіне келетін
болсақ, ол стек арқылы өте сирек беріледі, көбінесе регистр арқылы
беріледі.
Нақты мысал ретінде А адресінен бастап бірінші параметр (А) адресі
және екінші параметр (N) мәні стек арқылы берілетін шартымен зерденің N
байтын нольдейтін NULL(A,N) процедурасын сипаттаймыз. Процедура сипаттамасы
оң жақта келтірілген, ал сол жақта 100-байтты х массиві нольденетін
NULL(Х,100) жолдауды жасалуы көрсетілген.
X DB 100 DUP(?)
. . .
; NULL(X,100) шақыру
LEA AX, X
PUSH AX ;х адр. (стек
MOV AX, 100
PUSH AX ;100(стек
CALL NULL
. . .
NULL PROC
PUSH BP ;"кіретін" әрекеттер
MOV BP, SP
PUSH BX ;ВХ және СХ-ті құтқару
PUSH CX ;(ВРст "үстінде")
MOV CX, [BP+4] ;CX=A
MOV BX, [BP+6] ;BX=N
NULL1: MOV BYTE PTR[BX], 0 ;нольдеу
ІNC BX ;А(0, . . N-1)
LOOP NULL1
POP CX ;СХ және ВХ-ті қайта құру.
POP BX
POP BP ;"шығатын" әрекеттер
RET 4
NULL ENDP
9.5. Процедуралардың жергілікті берілгендері.
Көптеген процедураларда жергілікті берілгендері сақтаумен қиындық
тумайды – олар үшін регистрлерде жергілікті. Бірақта егер процедурада
жергілікті берілгендер көп болса, оларға орында қай жерден бөлу керек деген
сұрақ туындайды. Әрине, берілгендер сегментінде бөлуге болады, бірақта ол
жаман, өйткені уақыттың көп бөлігін зерденің бұл орны жай өседі. Бұл орынды
стекта бөлген жақсы: процедураға кірерде стек шынында жергілікті
берілгендерге керекті байт саны алынып, ал шығар алдында бұл орын
босатылады. Мұндай жағдайда зердедегі орын тек процедура орындалу уақытына
ғана алынады.
Стекта мұндай орынды "алуды" параметрлерді регистр арқылы да, стек
арқылы да процедураға бергенде жасауға болады. ол үшін стекта ВР
регистрінің дәл қазіргі мәнін еске сақтап оны стек шынына орнату керек.
Осыдан кейін SP стек көрсеткішінің мәнін "алатын" байт санына азайту керек.
Мысалға, егер қандайда Р процедурасына 3 байт керек болса (айталық 2 байт А
жергілікті айнымалы үшін және 1 байт В жергілікті айнымалыға), сонда
көрсетілген әрекеттер келесі командалармен жасалады.
P PROC SP( -3 жергілікті
PUSH BP -2 берілгендер
MOV BP, SP
SUB SP, 3
. . .
Осыдан кейін жергілікті берілгендерге жету [BP-k] түріндегі көрініс
көмегімен жүзеге асады; мысалға [BP-2] –бұл А жергілікті айнымалы адресі,
ал [BP-3] –В жергілікті айнымалының адресі.
Процедура жұмысын аяқтағанда мынадай әрекеттерді жасау керек:
. . .
MOV SP, ... жалғасы
Ұқсас жұмыстар
Пәндер
- Іс жүргізу
- Автоматтандыру, Техника
- Алғашқы әскери дайындық
- Астрономия
- Ауыл шаруашылығы
- Банк ісі
- Бизнесті бағалау
- Биология
- Бухгалтерлік іс
- Валеология
- Ветеринария
- География
- Геология, Геофизика, Геодезия
- Дін
- Ет, сүт, шарап өнімдері
- Жалпы тарих
- Жер кадастрі, Жылжымайтын мүлік
- Журналистика
- Информатика
- Кеден ісі
- Маркетинг
- Математика, Геометрия
- Медицина
- Мемлекеттік басқару
- Менеджмент
- Мұнай, Газ
- Мұрағат ісі
- Мәдениеттану
- ОБЖ (Основы безопасности жизнедеятельности)
- Педагогика
- Полиграфия
- Психология
- Салық
- Саясаттану
- Сақтандыру
- Сертификаттау, стандарттау
- Социология, Демография
- Спорт
- Статистика
- Тілтану, Филология
- Тарихи тұлғалар
- Тау-кен ісі
- Транспорт
- Туризм
- Физика
- Философия
- Халықаралық қатынастар
- Химия
- Экология, Қоршаған ортаны қорғау
- Экономика
- Экономикалық география
- Электротехника
- Қазақстан тарихы
- Қаржы
- Құрылыс
- Құқық, Криминалистика
- Әдебиет
- Өнер, музыка
- Өнеркәсіп, Өндіріс
Қазақ тілінде жазылған рефераттар, курстық жұмыстар, дипломдық жұмыстар бойынша біздің қор #1 болып табылады.
Ақпарат
Қосымша
Email: info@stud.kz