GS>> Это я понял. Hо "развёрнутая" запись подразумевает, что можно GS>> попробовать что-то вроде GS>> mov pc,r1
да хоть add pc, r1, r0, lsl #2 что означает PC = R1 + (R0 << 2)
GS>> Т.е. "настоящий" ret, вида GS>> pop pc GS>> не реализован?
А почему ты только его считаешь "настоящим"? Других не видел? Скажем, на той же PDP11 команда ret была всего лишь синонимом RTS PC а команда call dst - синонимом JSR PC,dst Т.е. "НАСТОЯЩИЙ" ret у PDP11 был таким себе частным случаем более общей команды возврата из подпрограммы.
PDP11: действия JSR reg, addr (Jump to SubRoutine) temp <- addr // причём addr может иметь разные методы адресации push reg reg <- PC pc <- temp
действия RTS reg PC <- reg pop reg
Итого JSR PC, addr заталкивал в стек PC и заносил в PC адрес подпрограммы а RTS PC восстанавливал PC из стека, это дело имело синонимы call и ret, но это был не единственный вариант. Никто не мешал сделать такое
jsr R5, subr ; r5 сохраняем в стеке, а в PC заносим... .word 10 ; Ну не совсем-то адрес возврата. Скорее .word var1 ; просто адрес следующей ячейки памяти :-) .word var2 mov #3, R0 ; это уже пошли следующие инструкции
subr: mov (R5)+,R0 mov (R5)+,R1 mov (R5)+,R2 ; R5 указывает на 'mov #3, R0' выше $1: mov (R1)+,(R2)+ sob R0,$1 rts r5 ; тем самым в PC заносится откорректированное автоинкрементами ; значение, а r5 восстанавливется из стека
И форма JSR с регистром, отличающимся от PC, использовалась довольно широко - при программировании на ассемблере а также внутри кода, сгенерированного разными компиляторами. В частности, используемый мной C-компилятор при помощи jsr R5,... обращался к своим внутренним, "служебным", подпрограммам, а у фортрана всё соглашение о вызовах стояло на jsr R5
Однако недостатки есть. Во-первых, нет особого счастья в том, что можно использовать _любой_ регистр. Во-вторых, при вызове "листовой" подпрограммы, да ещё и при наличии свободного регистра (в котором можно было бы сохранить PC) мы всё равно имеем обращение к стеку, на котором зря теряем время.
Но то был классический CISC, а ARM всё-таки RISC. Вот и тут одна CISC-овая команда JSR распалась на две, причём по дороге одна половинка слегка сократилась в возможностях, а другая - расширилась (точнее, получила возможность выполняться разными способами). bl выполняет первую часть, сохраняет PC aka R15 в специально выделенном для этой цели регистре LR (link register) aka R14 если функция была листовая, то возврат можно будет выполнить примитивным mov pc, lr
Кстати, при большом желании можно после вызова командой bl иметь команду перехода на обработчик ошибок, по ходу подпрограммы при помощи команд movne pc, lr moveq pc, lr и так далее делать возврат по ошибке а в конце командой add pc, lr, #4 сделать "номральный" возврат.
И только в случае необходимости вызова других подпрограмм из данной подпрограммы нужно сохранить lr где-то, причём "восстанавливать" его может и не понадобится. При сохранении его в регистре возврат можно сделать при помощи mov pc,еще_регистр, а при сохранении в стеке -- "восстановить" прямо в pc, Алексей Бойко показал как это делается командами ldm/stm Ничего лишнего при этом не делается, всё неплохо продумано. Довольно удобно и то, что команда ldmea _одна_ может восстанавливить все необходимые регистры и осуществить возврат из подпрограммы. А в силу того, что все команды условные, то можно "дёшево" натолкать таких возвратов по коду, не надо делать условный переход куда-то на восстановление/возврат, можно сделать всё на месте.
А что касается "забывчивости", то для профессионального писателя на ассемблере, который помнит всё дерево вызовов проекта и в голове спокойно строит компилированный стек -- то, что при модификации листовой подпрограммы в нелистовую надо позаботиться о сохранении/восстановлении lr это так, детские игрушки. С компилятор об этом помнит всегда.
wbr,