Reverse a string in place
The same program in M68K, MIPS, RISC-V, Z80.
A string reversed where it sits, with no second copy of it anywhere. Two addresses start at the two ends of the string, swap the bytes they point at, and walk towards each other until they meet in the middle.
Each swap writes two character bytes. The loop makes half as many swaps as there are characters, rounded down, and never writes the zero terminator. It needs no count: it stops when the two addresses meet or cross. This example starts with a nonempty string.
default rel
global _start
section .data
text: db "assembly", 0
LEN equ $ - text - 1 ; eight, without the terminator
section .text
_start:
lea rsi, [text] ; left, at the first character
lea rdi, [text + LEN - 1] ; right, at the last
.swap:
cmp rsi, rdi
jae .done ; they have met or crossed
mov al, [rsi]
mov bl, [rdi]
mov [rsi], bl ; the two bytes, exchanged
mov [rdi], al
inc rsi
dec rdi
jmp .swap
.done:
mov rax, 60
xor rdi, rdi
syscall
Type 402000 into the memory panel. The nine bytes read 79 6C 62 6D 65 73 73 61 00: ylbmessa,
and the terminator still sitting exactly where it was.
Keeping the zero out of the reversal is what LEN equ $ - text - 1 is for. $ - text is nine, every
byte the db line produced, and the - 1 takes the terminator back off, leaving eight characters to
reverse. Drop the - 1 and the zero gets swapped to the front, where it turns the string into an
empty one.
jae uses an unsigned comparison of the two addresses. The comparison is at the top of the loop,
before either byte is read. With an odd number of characters, the addresses meet on the middle
character, which stays where it is. With an even number, they cross after the last swap. jae
stops the loop in both cases. For a single character, the addresses start together, so there is
no swap.
Your turn
Reverse "rocket" in place. The starting addresses are set up for you: rsi points to r,
and rdi points to t, the last character before the zero. Write the loop that compares the
addresses, swaps their bytes through al and bl, moves both addresses inward, and repeats.
Use Test to check that memory at 0x402000 reads 74 65 6B 63 6F 72 00:
tekcor followed by the original zero terminator. You can also inspect those bytes in the
memory panel after running.
default rel
global _start
section .data
text: db "rocket", 0
LEN equ $ - text - 1
section .text
_start:
lea rsi, [text]
lea rdi, [text + LEN - 1]
.swap:
; Stop when rsi has met or passed rdi.
; Swap the two character bytes, then move the addresses inward.
.done:
mov rax, 60
xor rdi, rdi
syscall
Show solution
default rel
global _start
section .data
text: db "rocket", 0
LEN equ $ - text - 1
section .text
_start:
lea rsi, [text]
lea rdi, [text + LEN - 1]
.swap:
cmp rsi, rdi
jae .done
mov al, [rsi]
mov bl, [rdi]
mov [rsi], bl
mov [rdi], al
inc rsi
dec rdi
jmp .swap
.done:
mov rax, 60
xor rdi, rdi
syscall