Как преобразовать назначение и разыменование дальнего указателя Turbo-C в сборку x86? - PullRequest
0 голосов
/ 02 мая 2019

У меня будет тестирование сборки, и у меня возникнет вопрос по поводу указателей сборки. Я пытаюсь выполнить упражнение, но не могу его решить.

Рассмотрим утверждения в C:

int x=100, y=200;
int far *ptx;
int far *pty;

при условии, что инструкции уже выполнены:

ptx=&x;
pty=(int *)malloc(sizeof(int));   

мой вопрос о том, как кодировать следующие пункты в сборке:

  1. ptx=pty
  2. *ptx=*pty

1 Ответ

5 голосов
/ 02 мая 2019

Должны ли эти декларации быть в глобальном масштабе? Если это так, в статическом хранилище для переменных C будут метки asm. Если нет (локально внутри функции), они будут в стеке и IDK, как они ожидают, что вы будете знать, в каком смещении от BP они будут.

В любом случае, это 32-битные сегменты seg: off (с прямым порядком байтов, поэтому смещены в младшие 16 бит) дальние указатели, поэтому копирование одного в другое - это просто 4-байтовая копия, которую вы можете сделать с 2 целочисленными загрузками + магазины.

Переменные-указатели (когда они не оптимизируются вне или в регистр) сохраняют само значение указателя в памяти , как int или long. В C, когда вы делаете *pty, компилятор должен загрузить значение указателя в регистры, затем выполнить другую загрузку указанной памяти.


Я собираюсь предположить, что DS относится к сегменту данных, где сами значения указателя хранятся в памяти. И это sizeof(int)=2, потому что это кажется вероятным для 16-битной реализации языка Си.

Для разыменования и загрузки памяти, на которую указывает pty, то есть *pty, вам необходимо загрузить сегментную часть указателя детали в регистр сегмента, а смещенную часть - в SI, DI или BX ( регистры, которые можно использовать как часть режима адресации). В x86 есть соответствующие инструкции, например, les / lds.

Поскольку мы, вероятно, не хотим изменять DS, я просто буду использовать ES. (Различные ассемблеры используют разный синтаксис для переопределения сегментов, например [es: di] для NASM, но я думаю, может быть es:[di] для TASM.)

;; *ptx = *pty
;; clobbers: ES, DI, and AX
; load *pty
    les  di, [pty]        ; load pty  from [DS:pty] into ES:DI
    mov  ax, es:[di]      ; load *pty into AX

; store *ptx
    les  di, [ptx]        ; load ptx  from [DS:ptx] into ES:DI
    stosw                 ; store to *ptx from AX

STOSW сохраняет AX в ES: DI и увеличивает или уменьшает DI в соответствии с флагом направления, DF. Нас не волнует значение DI после выполнения этой инструкции, но стандартное соглашение о вызовах для Turbo C ++ (и современные соглашения x86) гласит DF=0 (увеличение вверх) при входе / выходе функции.

Используйте обычный mov с переопределением другого сегмента, если вы еще не узнали о строковых инструкциях.

(@ MichaelPetch говорит, что DS обычно сохраняется в 16-разрядных соглашениях о вызовах в реальном режиме, но ES можно свободно перемещать, не сохраняя и не восстанавливая его, так что, очевидно, я догадался.)


Или, если вы можете заткнуть DS и ES, вы можете использовать MOVSW . Использование push / pop ds для сохранения / восстановления было бы большим количеством инструкций. (Но все же меньший размер кода)

;; assuming DS is correct for referencing static data like [pty]
    les  di, [pty]        ; load pty  from [DS:pty] into ES:DI
    lds  si, [ptx]        ; load ptx  from [DS:ptx] into DS:SI
    movsw                 ; copy a word from [DS:SI] to [ES:DI]

Обратите внимание, что я использовал lds секунду , потому что я предполагаю, что оба глобала в статическом хранилище доступны через входящее значение DS, а не то, какое значение сегмента является частью другого дальнего указателя .

Если бы у вас была «огромная» или «большая» модель памяти (или другая модель, где известно, что не все статические данные помещаются в один сегмент размером 64 КБ), это было бы более сложно, но ваш вопрос ничего не показал о где ptx и pty действительно хранятся.


Кроме того, я предполагаю, что вы не должны оптимизировать их, основываясь на том, как они недавно были назначены , хотя вопрос показывает, на что они указывают.

Если вы знаете ptx = &x, то вам не нужно загружать ptx из памяти, вы можете просто mov [x], ax (опять-таки, предполагая модель кода, в которой статические данные, такие как x, доступны через DS).

Кроме того, нет смысла читать из *pty, когда он указывает на только что malloc хранилище, потому что оно не инициализировано. Другой способ будет иметь смысл. Я, вероятно, слишком анализирую это.

...