Прежде всего, вы не можете отлаживать ветвление, пока не исправите Как загрузить один байт с адреса в сборке - вы загружаете 4 байта символов и сравниваете все это 32-битное значениепротив 'a'
и так далее. Используйте movzx
вместо mov ebx, LocalBuffer[esi]
, потому что это массив char
.
Если вы выполняли пошаговый код в отладчике, возможно, вы заметили, что все4 байта ebx
не равны нулю. Вот почему ваши cmp / ветки не работают или делают то, что вы ожидаете.
@ zx485 объяснил общий случай цепочки ветвей, через которые вы проходите, пока вы не сможете определенно принять или отклонить ввод.
Но вы также можете упростить это, используя эффективные проверки диапазона, используя прием без знака сравнения. Например, Обратный инжиниринг asm с использованием sub / cmp / setbe обратно в C? Моя попытка компилирования по ветвям показывает, как это работает для только нижнего регистра ASCII.
Еще лучше, ASCII удобно спроектирован так, что диапазоны AZ и az совмещаются с каждымдругой, и не пересекайте границу %32
, поэтому вы можете перевести байт в нижний регистр с помощью c |= 0x20
или в верхний регистр с помощью c ^= ~0x20
. Тогда у вас есть только один диапазон для проверки буквенных символов.
OR
с 20h переводит символы верхнего регистра в нижний регистр и не превращает любые не алфавитные символы в нижний регистр, так что вы можетесделайте это с копией своего регистра.
См. В чем идея ^ = 32, которая преобразует строчные буквы в верхние и наоборот? и особенно Как получить доступ кмассив символов и меняйте строчные буквы на прописные, и наоборот для встроенного asm MSVC, который зацикливается на массиве символов и проверяет алфавитный или нет.
Убедитесь, что вы не уничтожаете свою единственную копиюпотому что вам все еще нужно считать верхний отдельно от нижнего;вы просто создаете временную ветвь. Если вы не хотите избегать неиспользуемых позиций в вашем массиве счетчиков, то, возможно, вы захотите c - 'A'
в качестве индекса вашего массива. Но, вероятно, нет, если у вас есть один массив для всех символов и цифр, которые вы хотите посчитать.
Пример
Для структуры цикла у меня символы вне диапазона перепрыгивают через Do Something
часть, достигнув условия цикла сравнения / ветвления. Приращение загрузки и индекса происходит на каждой итерации, независимо от загруженного символа.
Обратите внимание, что каждый символ, который не входит ни в один из диапазонов, является нецифровым и не буквой. Не имеет смысла выделять целую ветвь без цифр отдельно от цели ветвей без букв, потому что это не то, что вы понимаете. Тем не менее, у вас может быть разветвленная цифра и буква в разных местах.
_asm
{
xor esi, esi ; i=0
Loop1: ; do {
; load from the array *inside* the loop.
movzx ebx, byte ptr LocalBuffer[esi]
inc esi ; ebp = buf[i++]
; check for digits first
lea eax, [ebx - '0']
cmp al, 9
jbe CharCount ; if (c-'0' <= 9) goto CharCount
; non-digits fall through into checking for alphabetic
mov eax, ebx
or eax, 20h ; force to lower-case
sub eax, 'a' ; subtract start of the range
cmp al, 'z'-'a' ; see if it was inside the length of the range (unsigned)
ja skipCount
; in the common case (alphabetic characters), fall through into CharCount
CharCount:
; EBX still holds the character value, zero-extended
add byte ptr [counts + ebx], 1 ;Do something
; or use [counts + ebx*4] if you have an int array.
skipCount: ; rejected characters jump here, skipping count increment
cmp esi, 127
jb Loop1 ; } while(i<127)
}
Вам не нужно тратить второй регистр на другой счетчик цикла (ECX), когда у вас уже есть ESI. В любом случае cmp/jb
более эффективен, чем инструкция loop
.
Я думаю, что мы можем сохранить одну инструкцию, выполнив сначала вычитание (поэтому мы все равно можем использовать lea
для копирования и вычитания), нотогда мы должны очистить бит 0x20 вместо его установки, чтобы мы имели дело с прописными буквами.
;; untested, but I think this is correct, too, using LEA+AND instead of MOV+OR+SUB
lea eax, [ebx - 'A']
and eax, ~20h ; clear the lower-case bit
cmp al, 'Z'-'A' ; 25, same as 'z'-'a' of course.
ja skipCount
c - 'A'
= 0x20 для c='a'
. Коды символов после 'Z'
, но до 'a'
дают меньшие результаты, поэтому очистка бита 0x20
не может дать нам ложноположительный результат.
PS: если это та же проблема гистограммы, то вызадавал предыдущие вопросы о том, что вам не нужно фильтровать во время чтения, просто сделайте, чтобы в вашем массиве счетчиков было 256 элементов (для каждого возможного значения uint8_t
), а затем зацикливайте только те, которые вы хотите напечатать.
Если вы получали segfaults, используя ebx
в качестве индекса, это потому, что вы загрузили 4 байта (большое целое число) вместо расширяющего ноль. Мы уже исправили эту ошибку в предыдущих версиях вашего вопроса.
Также, как я ранее объяснил в комментариях , вам не нужно копировать строковый ввод в LocalBuffer
, просто сделайте char *bufptr = Buffer;
, а во встроенном ассемблере выполните mov esi, bufptr
, чтобы получить этоуказатель в регистр. Это неэффективно, но гораздо лучше, чем копировать весь массив. Особенно для подсчетов.
Или https://godbolt.org/z/QszVMf показывает, как получить доступ к членам класса из встроенного asm.