Строки представляют набор символовю Если нам надо определить строку, то мы можем указать набор символов в одинарных или двойных кавычках:
.data text byte "Hello" .code main proc mov al, text ; в регистре AL числовой код символа H - 72 ret main endp end
Здесь переменная text представляет набор из пяти байтов. Если мы поместим переменную, которая представляет набор байт, в регистр, то он будет содержать код первого байта.
Также можно через запятую перечислять числовые коды символов, которые входят в строку:
.data text byte 72, 101, 108, 108, 111 .code main proc xor rax, rax mov al, text ; в регистре AL числовой код символа H ret main endp end
Распространенным типом строк являются строки, которые оканчиваются на нулевой байт. Что может быть очень удобно при операциях со строками, поскольку мы знаем где у нее завершение. И, например, при взаимодействии с кодом C/C++ обычно требуется, чтобы строки завершались нулевым байтом - признаком завершения строки. В этом случае при определении строк в ассемблере можно в конце строки дописать числовой код символа:
.data text byte "Hello", 0
Здесь к строке Hello добавляется нулевой байт.
Используем нулевой байт для подсчета количества символов в строке:
.data text byte "Hello", 0 .code main proc lea rbx, text xor rax, rax ; в регистре RAX по умолчанию 0 startWhile: cmp byte ptr [rbx+rax*1], 0 ; сравниваем значение по адресу [rbx+rax*1] c 0 je endWhile ; если дошли до нулевого байта, выходим из цикла inc rax ; увеличиваем счетчик на 1 jmp startWhile ; переходим обратно к startWhile endWhile: ret main endp end
В данном случае регистр RAX выступает в качестве счетчика - он хранит количество символов и по умолчанию равен 0.
В регистр RBX загружаем адрес строки text. На метку startWhile проецируем начило цикла, в котором будем подсчитывать количество символов.
Для подсчета с помощью инструкции cmp сравниваем значение по адресу [rbx+rax*1]
с нулевым байтом.
cmp byte ptr [rbx+rax*1], 0
В начале программы, поскольку RAX равен 0, то выражение [rbx+rax*1]
даст нам адрес первого символа в строке text. Если символ равен нулевому байту, то переходим к метке
endWhile, то есть фактически выходим из цикла
je endWhile
Если же символ по адресу [rbx+rax*1]
не нулевой байт, то увеличиваем значение в регистре RAX на 1 и переходим опять к метке startWhile. И таким образом, сравниваем следующий символ строки
text с нулевым байтом.
С одной строны, это удобно, мы всегда знаем, где у строки конец, и можем определить строку из произвольного количества символов. В то же время в каких=то ситуациях это может быть не оптимальный способ представления строки. Например, опять же, чтобы узнать количество символов, нам надо перебрать всю строку.
В строках с фиксированным количеством символов обычно первый символ указывает на количество символов:
.data text byte 5, "Hello" .code main proc lea rbx, text movzx rax, byte ptr [rbx] ; в регистре RAX - 5 ret main endp end
Однако в этом случае мы ограничены строками длиной 255 байт (из которых первый байт - длина строки). Также можно автоматически вычислять длину с помощью констант:
.data text byte textLength, "Hello World" textLength = $-text-1 .code main proc lea rbx, text mov rax, textLength ; в регистре RAX - 11 ret main endp end