Оказывается, вы действительно можете обрабатывать значения 3 цифры за раз. Сделанный таким образом, вы можете обрабатывать строки произвольной длины, не ограничиваясь размером регистра. Не знаете, почему вам может понадобиться, если инопланетяне не пытаются связаться с нами, используя строки с аскей четвертичными цифрами с огромной длиной. Может случиться.
Возможно сделать перевод в любом случае (справа налево или слева направо). Тем не менее, есть проблема:
Если вы обрабатываете RtL, вам нужно знать длину выходной строки перед запуском (чтобы вы знали, где писать цифры при их вычислении). Это ухищренно, но немного сложно. Проще всего, длина равна ((strlen (Q) + 2)/3) * 2. То, что почти получает его. Однако в ряде случаев вы можете получить пустое место в начале. «1», а также «10» даст пустое место. «20» не будет. Правильное значение может быть вычислено, но это раздражает.
Аналогичным образом, обработка LtR имеет аналогичную проблему. У вас нет проблемы с поиском, где писать цифры, но подумайте: если строка для преобразования «123», то преобразование прост (33 восьмеричное). Но что, если вы начнете обрабатывать, а полная строка - «1231» (155 октал)? В этом случае вам нужно обработать его как «0» (01 55). IOW, цифры могут обрабатываться группами по 3, но вам нужно обрабатывать начальный случай, когда количество цифр не равномерно делится на 3.
Публикация решений домашней работы, как правило, я избегаю. Однако я сомневаюсь, что вы собираетесь превратить это в свое «решение», и это (едва) возможно, что Google может отправить кого-то здесь, кому что-то похожее.
Несколько вещей, чтобы отметить:
- Этот код предназначен для вызова из C помощью азЬсаИ Microsoft (это сделано тестирование проще) и скомпилированы с MASM.
- Хотя это написано на 32-битной (моей среде), нет ничего, что особенно требовало бы 32-битного в нем. Поскольку вы сказали, что используете таргетинг на 8086, я пытался избежать каких-либо «расширенных» инструкций. Преобразование до 16 бит или даже 64 бит не должно представлять большой проблемы.
- Он обрабатывается слева направо.
- Как и в любой хорошо написанной рутине, он проверяет свои параметры. Он выводит строку нулевой длины при ошибке, например, недопустимые цифры во входной строке.
- Он выйдет из строя, если выходной буфер равен NULL. Я полагаю, я мог бы вернуть ошибку bool (в настоящее время возвращает void), но, ну, я этого не сделал.
- Я уверен, что код может быть более жестким (не всегда ли?), Но для «качества домашнего задания» это кажется разумным.
Помимо этого, эти комментарии должны разъяснять код.
.386
.model flat
.code
; Call from C via:
; extern "C" void __fastcall PrintOct(const char *pQuat, char *pOct);
; On Entry:
; ecx: pQuat
; edx: pOct
; On Exit:
; eax, ecx, edx clobbered
; all others preserved
; If pOct is zero bytes long, an error occurred (probably invalid digits)
@[email protected] PROC
; -----------------------
; If pOct is NULL, there's nothing we can do
test edx, edx
jz Failed
; -----------------------
; Save the registers we modify (except for
; eax, edx and ecx which we treat as scratch).
push esi
push ebx
push edi
mov esi, ecx
mov edi, edx
xor ebx, ebx
; -----------------------
; esi: pQuat
; edi: pOct
; ebx: zero (because we use lea)
; ecx: temp pointer to pQuat
; Reject NULL pQuat
test esi, esi
jz WriteNull
; -----------------------
; Reject 0 length pQuat
mov bl, BYTE PTR [esi]
test bl, bl
jz WriteNull
; -----------------------
; How many chars in pQuat?
mov dl, bl ; bl is first digit as ascii. Preserve it.
CountLoop:
inc ecx ; One more valid char
; While we're counting, check for invalid digits
cmp dl, '0'
jl WriteNull
cmp dl, '3'
jg WriteNull
mov dl, BYTE PTR [ecx] ; Read the next char
test dl, dl ; End of string?
jnz CountLoop
sub ecx, esi
; -----------------------
; At this point, there is at least 1 valid digit, and
; ecx contains # digits
; bl still contains first digit as ascii
; Normally we process 3 digits at a time. But the number of
; digits to process might not be an even multiple of 3.
; This code finds the 'remainder' when dividing ecx by 3.
; It might seem like you could just use 'div' (and you can),
; but 'div' is so insanely expensive, that doing all these
; lines is *still* cheaper than a single div.
mov eax, ecx
mov edx, 0AAAAAAABh
mul edx
shr edx, 1
lea edx, [edx+edx*2]
sub ecx, edx ; This gives us the remainder (0-2).
; If the remainder is zero, use the normal 3 digit load
jz LoadTriplet
; -----------------------
; Build a triplet from however many leading 'odd' digits
; there are (1 or 2). Result is in al.
lea eax, DWORD PTR [ebx-48] ; This get us the first digit
; If there was only 1 digit, don't try to load 2
cmp cl, 1
je OneDigit
; Load the other digit
shl al, 2
mov bl, BYTE PTR [esi+1]
sub bl, 48
or al, bl
OneDigit:
add esi, ecx ; Update our pQuat pointer
jmp ProcessDigits
; -----------------------
; Build a triplet from the next 3 digits.
; Result is in al.
; bl contains the first digit as ascii
LoadTriplet:
lea eax, DWORD PTR [ebx-48]
shl al, 4 ; Make room for the other 2 digits.
; Second digit
mov cl, BYTE PTR [esi+1]
sub cl, '0'
shl cl, 2
or al, cl
; Third digit
mov bl, BYTE PTR [esi+2]
sub bl, '0'
or al, bl
add esi, 3 ; Update our pQuat pointer
; -----------------------
; At this point
; al: Triplet
; ch: DigitWritten (initially zeroed when computing remainder)
ProcessDigits:
mov dl, al
shr al, 3 ; left digit
and dl, 7 ; right digit
; If we haven't written any digits, and we are
; about to write a zero, skip it. This deals
; with both "000123" and "2" (due to OneDigit,
; the 'left digit' might be zero).
; If we haven't written any digits yet (ch == 0), and the
; value we are are about to write is zero (al == 0), skip
; the write.
or ch, al
jz Skip1
add al, '0' ; Convert to ascii
mov BYTE PTR [edi], al ; Write a digit
inc edi ; Update pointer to output buffer
jmp Skip1a ; No need to check again
Skip1:
or ch, dl ; Both check and update DigitWritten
jz Skip2
Skip1a:
add dl, '0' ; Convert to ascii
mov BYTE PTR [edi], dl ; Write a digit
inc edi ; Update pointer to output buffer
Skip2:
; Load the next digit.
mov bl, BYTE PTR [esi]
test bl, bl
jnz LoadTriplet
; -----------------------
; All digits processed. We know there is at least 1 valid digit
; (checked on entry), so if we never wrote anything, the value
; must have been zero. Since we skipped it to avoid
; unnecessary preceding zeros, deal with it now.
test ch, ch
jne WriteNull
mov BYTE PTR [edi], '0'
inc edi
; -----------------------
; Write the trailing NULL. Note that if the returned string is
; 0 bytes long, an error occurred (probably invalid digits).
WriteNull:
mov BYTE PTR [edi], 0
; -----------------------
; Cleanup
pop edi
pop ebx
pop esi
Failed:
ret
@[email protected] ENDP
end
Я запустить строку с 1000000000 четвертичных цифр через него, а также все значения от 0-4,294,967,295. Кажется, работает.
Я приветствую наших новых 4-значных инопланетных повелителей.
Я не совсем понимаю намерения здесь. Вы планируете вводить четвертичную строку и нужно печатать ее как восьмеричную строку? Я предполагаю, что это школьный проект какого-то типа (зачем еще писать его в asm?), Поэтому использование таких функций, как itoa, не может быть и речи? –
Да, пользователь должен ввести четвертичную строку, и программа напечатает восьмеричную строку. –
«для 16-значного числа» - самое большое значение, которое может содержать 16 бит 8086 в одном регистре (десятичный) 65535 (5 цифр). Это (четвертичный) 33333333 (8 цифр) и (восьмеричный) 177777 (6 цифр). Если вы хотите поддерживать 16-значные номера, это будет интересный проект. Может быть простой способ вычислить восьмеричную цифру непосредственно из четвертичной цифры, но я ее не вижу. Каждая (3-битная) восьмеричная цифра будет содержать всю соответствующую (2-битную) цифру четверти, плюс часть цифры рядом с ней. Должно быть проще просто загружать все квантерное число в рег и разбирать. –