В чем разница между этими двумя формами встроенного ассемблера в C? - PullRequest
7 голосов
/ 18 января 2010

Справочная информация: передо мной была поставлена ​​задача написать программу сбора данных для Unitech HT630 , которая работает под управлением проприетарной операционной системы DOS, которая может запускать исполняемые файлы, скомпилированные для 16-битной MS DOS, хотя и с некоторыми ограничениями , Я использую компилятор Digital Mars C / C ++, который, кажется, работает очень хорошо.

Для некоторых вещей я могу использовать стандартные библиотеки C, но другие вещи, такие как рисование на экране устройства, требуют ассемблерного кода. Примеры сборки, приведенные в документации устройства, отличаются от того, как меня учили использовать встроенный код сборки в C / C ++. Для справки, BYTE в приведенных ниже примерах имеет тип unsigned char.

Пример кода примера, который мне дали:

#include <dos.h>

/* Set the state of a pixel */
void LCD_setpixel(BYTE x, BYTE y, BYTE status) {
  if(status > 1 || x > 63 || y > 127) {
    /* out of range, return */
    return;
  }
  /* good data, set the pixel */
  union REGS regs;
  regs.h.ah = 0x41;
  regs.h.al = status;
  regs.h.dh = x;
  regs.h.dl = y;
  int86(0x10, &regs, &regs);
}

Как меня всегда учили использовать встроенную сборку:

/* Set the state of a pixel */
void LCD_setpixel(BYTE x, BYTE y, BYTE status) {
  if(status > 1 || x > 63 || y > 127) {
    /* out of range, return */
    return;
  }
  /* good data, set the pixel */
  asm {
    mov AH, 41H
    mov AL, status
    mov DH, x
    mov DL, y
    int 10H
  }
}

Кажется, что обе формы работают, я еще не сталкивался с проблемой ни с одним из подходов. Считается ли одна форма лучше, чем другая, для программирования под DOS? Обрабатывает ли функция int86 что-то, что я не обрабатываю в своем собственном коде сборки во втором примере?

Заранее благодарю за любую помощь.

Ответы [ 6 ]

9 голосов
/ 18 января 2010

Когда вы используете вызов функции int86, это вызов библиотеки времени выполнения C, который устанавливает регистры и выдает функцию прерывания DOS int. Оба метода на самом деле одинаковы, за исключением одного: при использовании встроенного ассемблера код фактически внедряется в объектный код при компиляции и компоновке.

Встроенная сборка будет считаться более быстрой, поскольку при вызове библиотеки времени выполнения C для вас не требуются дополнительные затраты на вызов прерывания DOS. На вас лежит ответственность за обеспечение достаточного стекового пространства при использовании встроенной сборки, тогда как библиотека C Runtime заботится о выделении стекового пространства при настройке регистров до вызова функции int86.

int86 - это способ облегчить вызов прерываний DOS. Это было чрезвычайно популярно среди старого набора компиляторов Borland Turbo C и в Microsoft, я говорю о старых компиляторах до выхода Win 3.1.

Говоря о прерывании 0x10, которое отвечает за вывод видео, если я правильно помню, в то время некоторые BIOS уничтожили регистр bp, и обходной путь должен был сделать это:

__asm{
   push bp;
}
/* set up the registers */
int86(0x10, &regs, &regs);
__asm{
   pop bp;
}

Обширные функции BIOS вы можете найти в списке прерываний Ральфа Брауна здесь . Также может помочь HelpPC v2.1, найденный здесь .

1 голос
/ 18 января 2010

Вам следует обратиться к руководству по компиляторам, чтобы узнать, кто отвечает за восстановление значений регистров после встроенного раздела сборки. Поскольку ваши переменные присваиваются регистрам, непреднамеренные изменения значений могут привести к трудным для обнаружения ошибкам. int86 (0x10, & regs, & regs); сохраняет регистры и восстанавливает их после выполнения программного прерывания.

Некоторые компиляторы принимают инструкции для определения списка клоббера (регистры, которые должны быть сохранены и восстановлены). Обычно раздел ассемблера должен сохранять регистры и флаги, которые будут изменены с помощью push, и восстанавливать их с помощью pop, либо компилятором, либо вами. Поэтому первый пример должен быть предпочтительным.

1 голос
/ 18 января 2010

Оба фрагмента кода выполняют одно и то же.Большим преимуществом первого является то, что есть вероятность, что вы все равно сможете использовать его при переключении компиляторов.И что вы не должны топать регистр, который генератор кода компилятора 'C' использовал для другой цели.Что-то, о чем вы точно забыли позаботиться в своем фрагменте asm.

1 голос
/ 18 января 2010

При вызове int86 ваш код остается на C. В любом случае, он записывает пиксель, выполняя системное прерывание.

Если у вас есть много пикселей для записи, и вы начинаете серьезно сталкиваться с проблемами скорости, возможно, существует более прямой (и гораздо менее безопасный, но, возможно, стоящий) способ записи непосредственно в память пикселей .

1 голос
/ 18 января 2010

первая форма более читабельна, что тоже что-то значит; -)

если вы хотите знать, что int86 делает что-то за вашей спиной, просто скомпилируйте вашу программу и проверьте сгенерированный код сборки

0 голосов
/ 18 января 2010

Это не встроенная сборка, это C. Очень низкоуровневый C, использующий функцию, вызывающую прерывание, но все же C.

На этой странице имеется некоторая документация (для компилятора DJGPP ваша может работать по-другому), включая структуру, используемую для представления регистров. Он также отмечает:

Обратите внимание, что в отличие от __dpmi_int функция, запросы, которые проходят int86 и аналогичные функции специально обработанные, чтобы сделать их подходит для вызова реального режима прерывания из защищенного режима программы. Например, если конкретный подпрограмма принимает указатель в BX, int86 ожидает от вас поставить (защищенный режим) указатель в EBX. Поэтому int86 должна иметь конкретную поддержку для каждого прерывать и функционировать вы вызываете это путь. В настоящее время он поддерживает только подмножество всех доступных прерываний и функции [...]

...