2016-12-14 11 views
1

Мне нужно подготовить программу для процессора 8086, которая преобразует четвертичное число в восьмеричное.Преобразование четвертичного в восьмеричное. ASM 8086

Моя идея:

Умножив каждую цифру по экспонент 4 и добавить к регистрации. Позже проверьте самый высокий показатель 8, не превышающий сумму с первого шага. Разделите по показателям 8 до остатка равно 0. Каждый результат деления - одна цифра в восьмеричном. Но для 16-значного числа последний показатель 4 равен 4^15. Я полагаю, что это не оптимальный алгоритм.

Есть ли другой способ? Может быть, для двоичных и групп на 3 цифры.

+0

Я не совсем понимаю намерения здесь. Вы планируете вводить четвертичную строку и нужно печатать ее как восьмеричную строку? Я предполагаю, что это школьный проект какого-то типа (зачем еще писать его в asm?), Поэтому использование таких функций, как itoa, не может быть и речи? –

+0

Да, пользователь должен ввести четвертичную строку, и программа напечатает восьмеричную строку. –

+0

«для 16-значного числа» - самое большое значение, которое может содержать 16 бит 8086 в одном регистре (десятичный) 65535 (5 цифр). Это (четвертичный) 33333333 (8 цифр) и (восьмеричный) 177777 (6 цифр). Если вы хотите поддерживать 16-значные номера, это будет интересный проект. Может быть простой способ вычислить восьмеричную цифру непосредственно из четвертичной цифры, но я ее не вижу. Каждая (3-битная) восьмеричная цифра будет содержать всю соответствующую (2-битную) цифру четверти, плюс часть цифры рядом с ней. Должно быть проще просто загружать все квантерное число в рег и разбирать. –

ответ

1

Оказывается, вы действительно можете обрабатывать значения 3 цифры за раз. Сделанный таким образом, вы можете обрабатывать строки произвольной длины, не ограничиваясь размером регистра. Не знаете, почему вам может понадобиться, если инопланетяне не пытаются связаться с нами, используя строки с аскей четвертичными цифрами с огромной длиной. Может случиться.

Возможно сделать перевод в любом случае (справа налево или слева направо). Тем не менее, есть проблема:

Если вы обрабатываете RtL, вам нужно знать длину выходной строки перед запуском (чтобы вы знали, где писать цифры при их вычислении). Это ухищренно, но немного сложно. Проще всего, длина равна ((strlen (Q) + 2)/3) * 2. То, что почти получает его. Однако в ряде случаев вы можете получить пустое место в начале. «1», а также «10» даст пустое место. «20» не будет. Правильное значение может быть вычислено, но это раздражает.

Аналогичным образом, обработка LtR имеет аналогичную проблему. У вас нет проблемы с поиском, где писать цифры, но подумайте: если строка для преобразования «123», то преобразование прост (33 восьмеричное). Но что, если вы начнете обрабатывать, а полная строка - «1231» (155 октал)? В этом случае вам нужно обработать его как «0» (01 55). IOW, цифры могут обрабатываться группами по 3, но вам нужно обрабатывать начальный случай, когда количество цифр не равномерно делится на 3.

Публикация решений домашней работы, как правило, я избегаю. Однако я сомневаюсь, что вы собираетесь превратить это в свое «решение», и это (едва) возможно, что Google может отправить кого-то здесь, кому что-то похожее.

Несколько вещей, чтобы отметить:

  1. Этот код предназначен для вызова из C помощью азЬсаИ Microsoft (это сделано тестирование проще) и скомпилированы с MASM.
  2. Хотя это написано на 32-битной (моей среде), нет ничего, что особенно требовало бы 32-битного в нем. Поскольку вы сказали, что используете таргетинг на 8086, я пытался избежать каких-либо «расширенных» инструкций. Преобразование до 16 бит или даже 64 бит не должно представлять большой проблемы.
  3. Он обрабатывается слева направо.
  4. Как и в любой хорошо написанной рутине, он проверяет свои параметры. Он выводит строку нулевой длины при ошибке, например, недопустимые цифры во входной строке.
  5. Он выйдет из строя, если выходной буфер равен NULL. Я полагаю, я мог бы вернуть ошибку bool (в настоящее время возвращает void), но, ну, я этого не сделал.
  6. Я уверен, что код может быть более жестким (не всегда ли?), Но для «качества домашнего задания» это кажется разумным.

Помимо этого, эти комментарии должны разъяснять код.

.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-значных инопланетных повелителей.