Напишем программу по переводу строку в верхний регистр. Псевдокод такой программы мог бы выглядеть следующим образом:
i = 0 DO char = inStr[i] IF char >= 'a' AND char <= 'z' THEN char = char - ('a' - 'A') END IF outStr[i] = char i = i + 1 WHILE char == 0 PRINT outStr
Здесь предполагается, что строка завершается нулевым байтом (как например, в языке Си), то есть в данном случае строка представляет набор символов, которые заканчиваются нулевым символом 0 или \0. Соответственнои в цикле DO..WHILE считываем каждый символ из строки inStr, пока он не равен 0.
В цикле, если символ является строчным, то вычитаем из его числового кода ascii расстояние до его аналога в верхнем регистре (оно равно разнице 'a' - 'A'
).
Полученный символ передаем в генерируемую выходную строку outStr.
Реализация на ассемблере:
// Перевод строки в верхний регистр // // X0-X2 - параметры системных функций Linux // X3 - адрес генерируемой строки // X4 - адрес исходной строки // W5 - текущий обрабатываемый символ // X8 - номер вызываемой функции функций Linux .global _start _start: LDR X4, =instr // загружаем адрес входной строки LDR X3, =outstr // загружаем адрес строки, которая будет генерироваться // в цикле считываем по байту, пока адрес в регистре X1 не будет указывать на 0 loop: LDRB W5, [X4], #1 // загружаем в W5 символ и увеличиваем адрес в X4 на 1 CMP W5, #'z' // если символ W5 > 'z', переходим к метке endif B.GT endif CMP W5, #'a' // иначе если W5 < 'a', то также переходим к метке endif B.LT endif SUB W5, W5, #('a'-'A') // в остальных случаях конвертируем букву в верхний регистр endif: STRB W5, [X3], #1 // сохраняем символ в строку outstr и увеличиваем адрес в X3 на 1 CMP W5, #0 // завершаем обработку, если символ представляет 0 B.NE loop // но если символ не равен 0, переходим обратно к метке loop // Вывод строки на консоль MOV X0, #1 // Номер потока для вывода - 1 = StdOut LDR X1, =outstr // выводимая строка SUB X2, X3, X1 // получаем длину строки с помощью X3-X1 MOV X8, #64 // устанавливаем функцию Linux для вывода в поток SVC 0 // Вызываем функцию Linux // выход из программы MOV X0, #0 // 0 - код возврата из программы MOV X8, #93 // устанавливаем функцию Linux для выхода из программы SVC 0 // Вызываем функцию Linux .data instr: .asciz "Hello Metanit.com\n" outstr: .fill 19, 1, 0
Вначале загружаем адреса обоих строк
LDR X4, =instr LDR X3, =outstr
Выходная строка здесь определена как набор из 19 байт. А исходная строка определяется с помощью директивы .asciz
, поэтому к ней автоматически добавляется нулевой байт.
С помощью инструкции LDRB
считываем один байт из исходной строки и увеличиваем адрес, чтобы в следующий раз взять следующий символ.
LDRB W5, [X4], #1
Далее нам надо узнать, что за символ и нужно ли его переводить в верхний регистр (так как он уже может быть в верхнем регистре или это не буква):
CMP W5, #'z' B.GT endif CMP W5, #'a' B.LT endif
Если символ вне диапазона a-z, то переходим к метке endif
. Если же попадает в диапазон, выполняем вычитание для получения кода символа в верхнем регистре:
SUB W5, W5, #('a'-'A')
Далее после метки endif
инструкция STRB помещает байт из W5 (числовой код символа) в строку, на которую указывает адрес в регистре X3. При этом
к адресу из X3 добавляется 1, чтобы потом установить следующий символ:
STRB W5, [X3], #1 CMP W5, #0 B.NE loop
Кроме того, сравниваем текущий символ с 0, чтобы узнать об окончании строки. Если символ не 0, переходим обратно к метке loop
.
Далее устaнавливаются параметры для выводы с помощью функции Linux на консоль. Обратите внимание, как вычисляется длина строки:
SUB X2, X3, X1
X3 в конце программы хранит адрес последнего символа исходной строки. Соответственно если мы вычтем из него адрес начала строки - X1, то получим длину в байтах (каждый символ занимает один байт)
Исходная строка в данном случае "Hello Metanit.com\n", соответственно на консоли мы увидим "HELLO METANIT.COM"
Стоит отметить, что мы можем оптимизировать программу в следующем месте:
CMP W5, #'z' // если символ W5 > 'z', переходим к метке endif B.GT endif CMP W5, #'a' // иначе если W5 < 'a', то также переходим к метке endif B.LT endif
Но по сути если мы отнимем от кода текущего символа строки числовой код символа 'a', то мы получим расстояние между этими двумя символами. Если текущий символ строки представляет символ латинского алфавита в нижнем регистре, то разность будет в диапазоне от 0 до 25 (ведь в латинсокм алфавите 25 символов).
Разность меньше нуля означает, что текущий символ строки уже в верхнем регистре, либо представляет число, либо другой символ ASCII, который идет до символа 'a'. Однако если мы будем рассматривать разность как число без знака (которое не может быть отрицательным), то отрицательные значения в данном случае мы можем отбросить. И будет достаточно сравнивать, больше ли разность, чем число 25. Так, изменим программу следующим образом:
// Перевод строки в верхний регистр // // X0-X2 - параметры системных функций Linux // X3 - адрес генерируемой строки // X4 - адрес исходной строки // W5 - текущий обрабатываемый символ // W6 - разность кода текущего символа строки и кода символа 'a' // X8 - номер вызываемой функции функций Linux .global _start _start: LDR X4, =instr // загружаем адрес входной строки LDR X3, =outstr // загружаем адрес строки, которая будет генерироваться // в цикле считываем по байту, пока адрес в регистре X1 не будет указывать на 0 loop: LDRB W5, [X4], #1 // загружаем в W5 символ и увеличиваем адрес в X4 на 1 SUB W6, W5, #'a' // Узнаем, является ли символ символом в нижнем регистре CMP W6, #25 // сравниваем разность с 25 - для символов в нижнем регистре разность должна быть 0-25 B.HI endif // если больше 25 SUB W5, W5, #('a'-'A') // в остальных случаях конвертируем букву в верхний регистр endif: STRB W5, [X3], #1 // сохраняем символ в строку outstr и увеличиваем адрес в X3 на 1 CMP W5, #0 // завершаем обработку, если символ представляет 0 B.NE loop // но если символ не равен 0, переходим обратно к метке loop // Вывод строки на консоль MOV X0, #1 // Номер потока для вывода - 1 = StdOut LDR X1, =outstr // выводимая строка SUB X2, X3, X1 // получаем длину строки с помощью X3-X1 MOV X8, #64 // устанавливаем функцию Linux для вывода в поток SVC 0 // Вызываем функцию Linux // выход из программы MOV X0, #0 // 0 - код возврата из программы MOV X8, #93 // устанавливаем функцию Linux для выхода из программы SVC 0 // Вызываем функцию Linux .data instr: .asciz "Hello Metanit.com\n" outstr: .fill 19, 1, 0
Мы можем пойти дальше и вообще убрать один из переходов и сделать программу более простой:
.global _start _start: LDR X4, =instr // загружаем адрес входной строки LDR X3, =outstr // загружаем адрес строки, которая будет генерироваться // в цикле считываем по байту, пока адрес в регистре X1 не будет указывать на 0 loop: LDRB W5, [X4], #1 // загружаем в W5 символ и увеличиваем адрес в X4 на 1 SUB W6, W5, #'a' // Узнаем, является ли символ символом в нижнем регистре CMP W6, #25 // сравниваем разность с 25 - для символов в нижнем регистре разность должна быть 0-25 SUB W6, W5, #('a'-'A') // конвертируем букву в верхний регистр CSEL W5, W6, W5, LS // если разность символов меньше 25, W5 = W6 STRB W5, [X3], #1 // сохраняем символ в строку outstr и увеличиваем адрес в X3 на 1 CMP W5, #0 // завершаем обработку, если символ представляет 0 B.NE loop // но если символ не равен 0, переходим обратно к метке loop // Вывод строки на консоль MOV X0, #1 // Номер потока для вывода - 1 = StdOut LDR X1, =outstr // выводимая строка SUB X2, X3, X1 // получаем длину строки с помощью X3-X1 MOV X8, #64 // устанавливаем функцию Linux для вывода в поток SVC 0 // Вызываем функцию Linux // выход из программы MOV X0, #0 // 0 - код возврата из программы MOV X8, #93 // устанавливаем функцию Linux для выхода из программы SVC 0 // Вызываем функцию Linux .data instr: .asciz "Hello Metanit.com\n" outstr: .fill 19, 1, 0
Ключевой момент здесь
CSEL W5, W6, W5, LS
Эта инструкция помещает в W5 символ из W6, если верно условие LS. Иначе в W5 остается то значение, которое было там ранее.
Условие LS устанавливается предыдущей инструкцией CMP - если она выявила, что в регистре W6 число меньше или равно 25 (то есть символ в нижнем регистре), то условие LS будет верно.