8086- почему мы не можем переместить немедленные данные в сегментный регистр?
В программировании сборки 8086 мы можем только загрузить данные в регистр сегмента, сначала загрузив их в регистр общего назначения, а затем мы должны переместить их из этого общего регистра в регистр сегмента.
Почему мы не можем загрузить его напрямую? Есть ли какая-то особая причина, по которой нас не пустят?
В чем разница между mov ax,5000H
а также mov ax,[5000H]
? Есть ли [5000h]
значит содержимое в ячейке памяти 5000х?
4 ответа
Помните, что синтаксис ассемблера (любой ассемблер) - это просто понятный человеку способ написания машинного кода. Правила того, что вы можете делать в машинном коде, зависят от того, как была сконструирована электроника процессора, а не от того, что синтаксис ассемблера мог бы легко поддерживать.
Итак, просто потому, что, похоже, вы могли бы написать mov DS, [5000h]
и что концептуально не кажется, что есть причина, по которой вы не должны быть в состоянии это сделать, это действительно вопрос "существует ли механизм, с помощью которого процессор может загружать регистр сегмента из содержимого ячейки памяти?"
В случае сборки 8086 я полагаю, что причина в том, что инженеры просто не создали электрический путь, который мог бы подавать сигнал из линий данных ввода-вывода памяти в строки, которые записывают в регистры сегментов.
Зачем? У меня есть несколько теорий, но нет авторитетных знаний.
Наиболее вероятная причина - просто упрощение конструкции: для этого требуются дополнительные проводки и шлейфы, и это достаточно редкая операция (это 70-е годы), которая не стоит недвижимости в чипе. Это не удивительно; 8086 уже вышел за борт, позволяя подключить любой из обычных регистров к ALU (арифметико-логическому устройству), что позволяет использовать любой регистр в качестве аккумулятора. Я уверен, что это было не дешево. Большинство процессоров в то время позволяли использовать только один регистр (аккумулятор) для этой цели.
Также возможно, что запись в регистр сегмента из чтения из памяти привела к нескольким странным крайним случаям, которые было трудно получить в схемах. В конце концов, регистр сегмента, который должен быть записан, может использоваться для адресации исходного операнда.
Что касается скобок, вы правы. Допустим, позиция памяти 5000h содержит число 4321h. mov ax, 5000h
помещает значение 5000h в топор, в то время как mov ax, [5000h]
загружает 4321h из памяти в топор. По сути, скобки действуют как *
оператор разыменования указателя в C.
Просто чтобы подчеркнуть тот факт, что сборка является идеализированной абстракцией того, что может делать машинный код, вы должны заметить, что эти две вариации - это не одна и та же инструкция с разными параметрами, а совершенно разные коды операций. Они могли бы использовать - скажем - MOV
для первого и MVD
(MoVe Direct адресовал память) для второго кода операции, но они, должно быть, решили, что синтаксис скобок легче запомнить программистам.
Машинный код x86 имеет только один код операции для перехода к Sreg. Этот код операции 8E /r
mov Sreg, r/m16
и разрешает регистр или источник памяти (но не сразу).
Вопреки некоторым утверждениям в других ответах, mov ds, [5000h]
работает очень хорошо, предполагая 16 байтов по адресу 5000h
храните полезное значение сегмента для режима, в котором вы находитесь. (Реальный режим, где они используются непосредственно как числа по сравнению с защищенными, где значения Sreg являются селекторами, которые индексируют LDT / GDT).
x86 всегда использует другой код операции для немедленной формы инструкции (с константой, закодированной как часть машинного кода), в отличие от версии регистра / источника памяти. например add eax, 123
собирается в другой код операции из add eax, ecx
, Но add eax, [esi]
такой же код операции add r, r/m32
код операции как add eax, ecx
просто другой байт ModR/M.
Список NASM, от nasm sreg.asm -l/dev/stdout
сборка плоского двоичного файла в 16-битном режиме и создание листинга.
Я отредактировал вручную, чтобы разделить байты в opcode modrm extra
, Все они являются однобайтовыми кодами операций (без дополнительных битов кода операции, занимающих место в поле /r байта ModRM), поэтому просто посмотрите на первый байт, чтобы увидеть, что это за команда, и обратите внимание, когда две инструкции используют один и тот же код операции.
address machine code source ; comments
1 00000000 BE 0050 mov si, 5000h ; mov si, imm16
2 00000003 A1 0050 mov ax, [5000h] ; special encoding for AX, no modrm
3 00000006 8B 36 0050 mov si, [5000h] ; mov r16, r/m16 disp16
4 0000000A 89 C6 mov si, ax ; mov r/m16, r16
5
6 0000000C 8E 1E 0050 mov ds, [5000h] ; mov Sreg, r/m16
7 00000010 8E D8 mov ds, ax ; mov Sreg, r/m16
8
9 mov ds, 5000h
9 ****************** error: invalid combination of opcode and operands
Поддерживая mov Sreg, imm16
для кодирования потребуется отдельный код операции. Это потребовало бы дополнительных транзисторов для декодирования 8086, и это заняло бы больше места кодирования кода операции, оставляя меньше места для будущих расширений. Я не уверен, какой из них был сочтен более важным архитектором (ами) 8086 ISA.
Обратите внимание, что 8086 имеет специальные mov AL/AX, moffs
коды операций, которые экономят 1 байт при загрузке аккумулятора с абсолютного адреса. Но он не мог сэкономить код операции для mov
Непосредственно к Срегу? Это дизайнерское решение имеет смысл. Как часто вам нужно перезагрузить сегментный регистр? Очень редко, и в действительно больших программах это часто не было бы с константой (я думаю). Но в коде, использующем статические данные, вы можете загружать / хранить аккумулятор по фиксированному адресу внутри цикла. (8086 имел очень слабую выборку кода, поэтому размер кода = скорость большую часть времени).
Также имейте в виду, что вы можете использовать mov Sreg, r/m16
для констант времени сборки только с одной дополнительной инструкцией (например, mov ax, 4321h
). Но если бы мы только имели mov Sreg, imm16
, значения сегмента переменной времени выполнения потребовали бы самоизменяющегося кода. (Очевидно, что вы бы не r/m16
Исходная версия.) Я хочу сказать, что если у вас будет только одна версия, это определенно будет версия из реестра / источника памяти.
О сегментных регистрах
Сегментные регистры не совпадают (на аппаратном уровне) с регистрами общего назначения. Конечно, как сказал Майк У. в комментариях, точная причина, по которой вы не можете сразу переместить непосредственное значение в регистр сегмента, известна только разработчикам Intel. Но я полагаю, это потому, что дизайн очень прост. Обратите внимание, что этот выбор не влияет на производительность процессора, поскольку операции с сегментным регистром выполняются очень редко. Итак, одна инструкция больше, а другая меньше не важна.
О синтаксисе
Во всех разумных реализациях синтаксиса ассемблера x86, mov reg, something
перемещает непосредственный номер something
в реестр reg
, Например:
NamedConst = 1234h
SomeLabel:
mov edx, 1234h ; moves the number 1234h to the register edx
mov eax, SomeLabel ; moves the value (address) of SomeLabel to eax
mov ecx, NamedConst ; moves the value (1234h in this case) to ecx
Закрытие числа в квадратных скобках означает, что содержимое памяти с этим адресом перемещается в регистр:
SomeLabel dd 1234h, 5678h, 9abch
mov eax, [SomeLabel+4] ; moves 5678h to eax
mov ebx, dword [100h] ; moves double word memory content from the
; address 100h in the data segment (DS) to ebx.
Я помню, как читал причину еще в тот день. Передо мной нет этого документа, поэтому, пожалуйста, прости меня за махание рукой.
Загрузка сегментного регистра из области памяти или константы включает циклы памяти. Если выравнивание памяти испорчено, чтение 16-битного значения может занять два цикла памяти. В промежутках между циклами значение регистров сегмента недопустимо. Теперь представьте, что вы возитесь с регистром сегмента стека, и происходит прерывание: вот ваша тележка; наслаждаться поездкой!