... написать программу, которая использует loop
для рекурсивной процедуры вместо использования условных выражений ...
Я считаю это интересной задачей, потому что она требуетнестандартного мышления.Он, вероятно, не имеет практического применения, но, тем не менее, имеет некоторые достоинства, подтверждающие концепцию.
Инструкция loop
кодируется с 8-разрядным смещением со знаком, что означает, что условный переход может перейти назад и вперед !В большинстве (если не во всех) случаях, когда loop
все еще используется сегодня, мы бы только прыгнули назад.
Помещение инструкции loop
сверху выглядит очень неестественно, но работает нормально.
Нижекод работает в 2 этапа
- подготовительный этап помещает в стек набор адресов возврата (рекурсивные вызовы)
- производительный этап выводит все эти адреса возврата и печатает текущий номер
Помещение inc count
перед call WriteInt
выставленным call WriteInt
как хвостовой вызов , и поэтому я могу заменить его на jmp WriteInt
.
Когда начальная фаза начинается, ECX
будет 0. Поэтому вместо использования переменной count в памяти я использовал для этой цели регистр ECX
.
Код защищен от вводабесконечный цикл и провоцирующее переполнение стека с помощью инструкции jecxz Done
.
jmp Start
; ----------------------
Recur:
loop .a ; Jumps N-1 times
jmp .b ; Jumps 1 time
.a: call Recur
.b: mov eax, ecx
inc ecx
jmp WriteInt ; Returns N times
; ----------------------
Start:
call ReadDec ; -> EAX (valid input is assumed)
mov ecx, eax ; The unsigned number N is [0,4GB-1]
jecxz Done ; In case N == 0
call Recur
Done:
Интересно, что написать это так же просто, используя loop
обычным способом, поэтому прыгать назад.Однако он требует дополнительного приращения на счетчике, и он прыгает намного больше (см. Сравнение ниже).
jmp Start
; ----------------------
Recur:
jmp .b ; Jumps N+1 times
.a: call Recur
mov eax, ecx
inc ecx
jmp WriteInt ; Returns N times
.b: loop .a ; Jumps N times
ret ; Returns 1 time
; ----------------------
Start:
call ReadDec ; -> EAX (valid input is assumed)
mov ecx, eax ; The unsigned number N is [0,4GB-1]
jecxz Done ; In case N == 0
inc ecx
jz Done ; IN case N == 4GB-1 (stack overflow for sure!)
call Recur
Done:
Ниже я сравнил оба метода.Я удалил вызовы на WriteInt по понятным причинам ...
LOOP FORWARD LOOP BACKWARD
jmp Start jmp Start
; ---------------------- ; ----------------------
Recur: Recur:
loop .a jmp .b
jmp .b .a: call Recur
.a: call Recur mov eax, ecx
.b: mov eax, ecx inc ecx
inc ecx ret
ret .b: loop .a
ret
; ---------------------- ; ----------------------
Start: Start:
mov ecx, 25000 mov ecx, 25000
call Recur inc ecx
call Recur
Фрагмент слева выполнен за 282 мкс, фрагмент справа - за 314 мкс.Это на 11% медленнее.