Путаница с расширением знака инструкции MIPS I-типа

Я изучаю инструкции MIPS, и когда я тестирую инструкции I-типа, которые должны подписать, расширяют немедленно, я смущен следующими результатами (все они выполняются в MARS):

  1. Скажем, у нас есть строка исходного кода ori $s1, $s2, 0xfd10, MARS дает базовую инструкцию на ассемблере ori $17, $18, 0x0000fd10. Это ожидаемо, так как ori должен обнулить 16-битное непосредственное значение. Если мы изменим только функцию ori на andi, то есть строку исходного кода andi $s1, $s2, 0xfd10, MARS выдаст почти ту же базовую инструкцию ассемблера andi $17, $18, 0x0000fd10. Однако, в отличие от ori, andi следует использовать расширение знака. Таким образом, основная инструкция на ассемблере должна быть andi $17, $18, 0xfffffd10.

andi также следует использовать нулевое расширение! Пожалуйста, игнорируйте первый вопрос.

  1. Когда я пытаюсь использовать slti rt, rs, imm, например, slti $s1, $s2, 0x8000, MARS отказывается выполнять строку и выдает сообщение об ошибке "0x8000": operand is out of range. Я не вижу причин, по которым непосредственное находится вне досягаемости. Если я немного изменю непосредственное значение, скажем, на slti $s1, $s2, 0x7fff, оно сработает, и немедленное значение будет расширено до 0x00007fff. Я ожидаю, что 0x8000 следует расширить до 0xffff8000. Что-то не так с моим пониманием?

№1 похоже на ошибку. Причина № 2 - избежать путаницы. Вы написали 0x8000, значит, вы хотели 0x8000, а не 0xffff8000.

Jester 09.12.2020 18:32
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
1 604
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Значения в исходном коде asm представляют собой фактические числовые значения, с которыми вы хотите работать, а не только битовые шаблоны, которые должны быть закодированы в инструкции.

0x8000 - это не то же самое число, что и 0xffff8000, поэтому ассемблер не дает вам изменить ваше значение расширением знака. Если вы хотите, чтобы машинный код конечной инструкции кодировал значение 0xffff8000, вы должны написать 0xffff8000 в исходном коде ассемблера для инструкций, которые расширяют свои непосредственные действия знаком.

В нашей системе записи позиционных значений для чисел существует бесконечное количество неявных старших 0 цифр слева от явных цифр. Итак, 0x8000 — это то же число, что и 0x00008000, и это число, которое ассемблер пытается представить как 16-битное непосредственное расширение со знаком.


Вы подходите к этому с точки зрения того, как кодируются инструкции I-типа. Но ассемблеры предназначены для обработки деталей кодирования за вас. Это часть смысла его использования. Допустим, вы пишете addiu $t0, $t1, -123, а ассемблер кодирует -123 как 16-битное непосредственное расширение со знаком.

Скажем, вы пишете ori $t0, $t0, -256, чтобы установить все биты выше младшего байта. Но ассемблер отклоняет это, потому что это не может быть закодировано как прямое расширение с нулевым расширением для ori, вместо того, чтобы молча оставлять старшие 16 битов неустановленными, как 0x0000ff00. Таким образом, вам не нужно запоминать, как каждая инструкция относится к своему непосредственному; ассемблер проверит это за вас. Это преднамеренная функция и хороший дизайн.

Особенно, если у вас есть большая программа, которая определяет некоторые константы времени сборки, а затем использует их различными способами: если настройка одного из этих значений приводит к невозможности кодирования инструкции, вы хотите знать об этом, а не молчаливо получать неверные результаты.

(И поскольку я использовал десятичные примеры, запись чисел в виде шестнадцатеричных числовых литералов ничего не меняет в отношении того, как ассемблер должен их обрабатывать.)


Однако, в отличие от ori, в andi следует использовать расширение знака.

Нет, в MIPS все 3 побитовые логические инструкции (ori/andi/xori) расширяют до нуля свои непосредственные. (Sign-extend был бы более полезен в большем количестве случаев для AND, разрешая маски только с несколькими нулями в младших битах, но это не то, как разработан MIPS. Хотя это сделало бы усечение ровно до 16 бит дороже.)

Документация типа https://ablconnect.harvard.edu/files/ablconnect/files/mips_instruction_set.pdf подтверждает andi нулевое расширение. Я не проверял официальные документы MIPS, но эта информация широко распространена в Интернете; вы также можете проверить, чтобы компиляторы использовали его таким образом для реализации uint16_t или чего-то еще.

Также инструкция andi vs. addi в MIPS с отрицательной непосредственной константой (покрывает MARS с включенными расширенными псевдоинструкциями, поэтому он создаст полное 32-битное значение в другом регистре, если вы используете andi со значением, которое не кодируется как 16-битный с нулевым расширением (немедленный)

Большое спасибо. Что касается ori, меня ввела в заблуждение конспект лекции с опечаткой. Я проверил различные таблицы mips, такие как inst.eecs.berkeley.edu//~cs61c/fa11/MIPS_Green_Sheet.pdf, и подтвердил, что ori также использует нулевое расширение. Я до сих пор не очень понимаю часть slti. slti $s1, $s2, 0xffff8000 также отказывается от MARS. И какое отрицательное число будет дополнено знаком slti? Если я использую sltiu $s1, $s2, 0x8000, ошибка та же. Я много искал, но не нашел объяснения процесса расширения знака числа, например 0x8000 вместо slti или sltiu.

codekiwi 09.12.2020 19:00

@codekiwi: я только что попробовал в MARS 4.5, и slti $s1, $s2, 0xffff8000 собрался, как и ожидалось, в 0x2a518000 slti $17, $18, 0xffff8000. Это работает, потому что 0xffff8000 можно представить как 16-битное целое число с расширенным знаком.

Peter Cordes 09.12.2020 19:08

Спасибо. Я снова открыл свой MARS 4.5, и slti $s1, $s2, 0xffff8000 заработало. Я понимаю, что 0xffff8000 можно представить как 16-битное целое число с расширенным знаком. Мой вопрос: поскольку 0x1000 может быть автоматически расширен ассемблером до 0x00001000, почему 0x8000 не может? Почему 0xffff8000 нужно вводить в источник явно вручную? Извините, если это глупый вопрос.

codekiwi 09.12.2020 19:21

Потому что 0x1000 и 0x00001000 — это одно и то же число, а 0x8000 и 0xffff8000 — нет.

prl 09.12.2020 19:23

@codekiwi: Когда я пишу 0xffff как числовую литеральную константу в C, ассемблере или чем-то еще, предполагается, что неявные старшие биты равны 0, т. е. это то же число, что и 0x0000ffff, а не -1. Вы пишете значение, которое хотите использовать в инструкции, а не кодировку, которую хотите использовать для инструкции MIPS I-типа. Вот что пытается объяснить первая часть моего ответа.

Peter Cordes 09.12.2020 19:24

Большое спасибо, Питер. Я сравниваю slti с lw, addi и начинаю понимать «значение, которое вы хотите, чтобы инструкция использовала», и «кодировку, которую вы хотите для инструкции MIPS I-типа». В этом листе данных просто говорится, что некоторые инструкции требуют расширения знака, и не дается никаких объяснений процесса, выполняемого ассемблером. В то время как учебник объяснил, что такое расширение знака, но не сочетает его с практикой. Спасибо за ваше терпение, помогая мне.

codekiwi 09.12.2020 19:55

@codekiwi: Лист данных, конечно, документирует, как ЦП обрабатывает машинный код, не более того. Понимание философии дизайна исходного языка типичных ассемблеров - это отдельная и совершенно другая вещь, и да, обычно не прописанная в документации. Тот факт, что числа являются числами, является своего рода неявным. Разные ассемблеры могут делать разные выборы, например. следует ли предупреждать и усекать или выдавать ошибку, когда число не помещается в непосредственное.

Peter Cordes 09.12.2020 20:01

Другие вопросы по теме