MIPS Подсчет количества единиц с учетом ввода пользователя (инструкция B не переходит в MARS) - PullRequest
1 голос
/ 19 февраля 2020

Я написал код в MIPS, который, как я думал, будет принимать пользовательский ввод (десятичное число), И это с 1 и увеличивать это на 1 и сдвигать битовую позицию влево на 1, пока число не достигнет 0, затем отобразить число 1 с десятичного числа в его двоичном представлении. Я не уверен, где я ошибся.

# Count number of 1s in a 32 Bit Number
#$t0 = user input
#$t1 = counter
.data
prompt: .asciiz "Enter Number: "
result: .asciiz "Number of 1s counted: "
n:  .word   0
counter:.word   0
    .text

#Prompt User for Number 
    li $v0, 4       #load text stored in v0
        la $a0, prompt  #print prompt
        syscall     #call prompt string

#Get user Input   
        li $v0, 5   #load number stored in v0
        la $t0, n   #load int into $t0
        syscall     #display integer

#Store user input in $t0 to do function
        move $t0, $v0   #Move value from $v0 to $t0 to modify
        la $t1, counter #load address of counter
        lw $t1, 0($t1)  #load counter to t1

AND:
    andi $t2, $t0, 1 #and user input($t0) with 1 and store in $t2
    beq  $t2, 1, loop #if t2 equals 0 branch
    srl  $t0, $t0, 1 #shift user input($t0) to right 1 position logically
    b AND       #branch this function

loop:
    add $t1, $t1, 1 #add 1 to the counter
    srl $t0, $t0, 1 #shift $to to the right 1 position logically with counter
    beqz $t0, display #If $t0 equals 0 send to display function
    b AND       #send back to AND function if not

display:
       li $v0, 4    #load text stored in v0
       la $a0, result   #print text from address a0
       syscall
       la $a0, ($t1)    #load the address of the counter to a0
       li $v0, 1      #load integer stored in v0
       syscall        #print final integer

Ответы [ 2 ]

1 голос
/ 19 февраля 2020

MARS, по-видимому, содержит ошибку ассемблера в b инструкциях, когда имя метки также является допустимой инструкцией mnemoni c. Это заканчивается как инструкция перехода к следующему.

Изменение имени метки с AND на AND_TOP привело к правильной сборке. Эраклон обнаружил, что с помощью инструкции j тоже сработало. (: вместо операндов устраняет неоднозначность токена как метки вместо инструкции, поэтому это ошибка в MARS, а не ошибка в вашем коде. clang прекрасно собирает ваш исходный код. Не что вы можете запустить его вне MARS, это зависит от системных вызовов MARS и ветвей без слотов.)

Я тестировал в Mars 4.5 под Java OpenJDK 1.8.0_232 на Arch GNU / Linux и воспроизвел результат @ Эраклона. (Но рассуждения в этом ответе неверны.)

Обе b AND инструкции собираются в 0x043fFFFF (с перечисленной инструкцией bgez $0, AND), поэтому цель перехода - это инструкция сразу после ветви . (Какой будет задержка на реальном MIPS). Как вычислить целевой адрес перехода и целевой адрес перехода? показывает, что инструкции относительного перехода MIPS имеют I-тип и расширяют знак на 16 бит непосредственно (младший 16 слова инструкции) и сдвиг влево на 2. (Или посмотрите на это без смещения как смещение в словах). Относительно конца интервала задержки ветвления. Таким образом, смещение -1 возвращает нас к инструкции после ветви.

Также обратите внимание, что 0x043f не является правильным кодом операции и регистровой кодировкой для bgez. Это должно быть 0x0401 для bgez $zero. (http://www.mrc.uidaho.edu/mrc/people/jff/digital/MIPSir.html показывает кодировки в двоичном виде). Согласно llvm-objdump -d (после сборки .word 0x043fffff с clang -target mips) это фактически кодирует 4 3f ff ff synci -1($1). Интересно, была ли ошибка MARS ORing в значении -1, которое было шире, чем следовало бы, перезаписывая несколько старших байтов?

(bgez $0 - это один из способов кодирования безусловной относительной ветви в MIPS. Другой способ - beq $0,$0, target. Отдельного кода операции не существует, нужно просто выбрать одну из b... машинных инструкций I-типа с вводными данными, которые всегда верны.)

MARS имитирует эта неправильно собранная инструкция как ветвь - всегда с закодированным смещением: она фактически проходит через ветвь . Или при включенном параметре «Настройки» -> «Задержка ветвления» команда после ветвления выполняется дважды: один раз как интервал задержки, один раз как цель ветвления.

Так что, очевидно, симулятор действительно не декодирует из машинного кода, даже если вы включаете настройки-> самоизменяющийся код. Или это просто дисплей, который сломан? IDK, на самом деле не имеет значения, это 100% ошибка, и вместо того, чтобы указывать на симптомы, кто-то должен просто посмотреть на источник MARS и исправить его.


Использование другого имени метки дает правильные результаты: первый b AND_TOP собирается в 0x0401fffb, а MARS перечисляет его как bgez $0, 0xfffffffb. (Обратите внимание на смещение цифры c в списке вместо имени метки AND.)

И оно имитируется правильно, причем эти ветви идут в нужное место.


I Я не проверял вашу логику c, но она кажется слишком сложной. Обратите внимание, что la $a0, ($t0) - это безумный способ написать move $a0, $t0. Очевидно, MARS это позволяет. Впрочем, не было никакой причины загружаться с counter; Вы можете обнулить регистр с помощью addu $t0, $zero, $zero или чего угодно еще. Или напишите это как move $t0, $zero.

Также это глупо:

    beqz $t0, display #If $t0 equals 0 send to display function
    b AND_TOP         #send back to AND function if not

display:

Просто bnez AND_TOP вместо условного перехода через безусловную ветвь.

Кроме того, ни Комментарий добавляет что-либо к пониманию. Если есть что сказать о , почему вы прыгаете, или значение semanti c (например, в терминах переменных высокого уровня, а не имен регистров), то укажите это в комментарии. , например, bnez $t0, count_loop # more bits left to count?

Конечно, как указывает @Eraklon, вся ваша логика ветвления c очень сложна. Просто выделите младший бит и добавьте его в счетчик, будь то ноль или единица.

Или, если вы заботитесь о производительности, замаскируйте четные и нечетные биты, сдвиг вправо на 1 и addu. Тогда у вас есть 16x 2-битные аккумуляторы, упакованные в регистр. Повторите с другой маской, пока у вас не появятся байты, затем либо продолжайте, либо используйте трюк умножения, чтобы байты суммировались в старший байт. (См. Быстрые вопросы и ответы по поп-счету здесь о переполнении стека для ответов bithack, или https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel. Ваш метод похож на https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetNaive, но более сложный. Есть варианты среднего уровня, например, очистка младший установленный бит и счетчик итераций для его обнуления.)

1 голос
/ 19 февраля 2020

Выполнение этого кода с помощью эмулятора MIPS MARS 4.5 показывает, что по какой-то пока неизвестной мне причине b AND просто не работает должным образом. Если вы напишите j AND, вместо этого все будет работать правильно.

РЕДАКТИРОВАТЬ: Как указал @Peter Cordes, это, скорее всего, ошибка ассемблера.

ПРИМЕЧАНИЕ : Вы можете удалить всю часть loop и сделать это в разделе AND:

and:
    andi $t2, $t0, 1 #and user input($t0) with 1 and store in $t2
    add $t1, $t1, $t2 #add the result it is either 0 or 1
    srl  $t0, $t0, 1 #shift user input($t0) to right 1 position logically
    beqz $t0, display #If $t0 equals 0 send to display function
    j and       #send back to AND function if not
...