Перейти к содержанию
    

Использование ассемблера в MinGW

Осваиваем инлайновский ассемблер в MinGW.

Текст функции

/******* Вывод байта в порт *******/
void outportb(unsigned addr,        // адрес регистра
                      unsigned char data)    // данные для записи
{
      asm(“mov %%eax, %2; \        // шаблон кода на ассемблере для
               out %1, %%al;”          // вывода байта в порт
               :                    // выходных переменных нет
               : “r” (addr), “r” (data)        // список входных операндов
               : “%%eax”, “%%al”);        // используемые регистры
}

 

1) Компилятор не желает передавать параметры через короткие регистры, даже для unsigned char (кто-нибудь знает какая, кстати, у него длина в Минималисте?). Поэтому используем eax.

 

2) Компилятор ругается на строчку с out (неправильный суффикс или операнд?), причём ругается именно на al (т.е. при замене out на mov - одно и тоже сообщение об ошибке). Непонятно :(

 

3) Тип void тоже почему-то не понимает... Придется вводить фиктивную выходную переменную типа int, например.

 

Плиз, помогите. Может у кого-нибудь были похожие ситуации и в другом gcc (не обязательно под Windows, для любого МК)? Передать, естеств., хотелось бы байт. Т.к. это для учебной лабы.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2) Компилятор ругается на строчку с out (неправильный суффикс или операнд?), причём ругается именно на al (т.е. при замене out на mov - одно и тоже сообщение об ошибке). Непонятно :(
У Вас строковый литерал (собственно ассемблерная вставка) распался на две строки.

Кроме этого я вижу тут следующие неприятности:

1. В as для i386 символом начала комментария является '#', а вовсе не точка с запятой (';') как у Вас. Ну еще, кажется, можно использовать /* вот такие */ комментарии, как в C. В вышеприведенном случае ассемблер должен ругаться на "мусор" после команды (начиная с символа ';').

2. Вы поместили сразу две ассемблерные команды в одну строку, поэтому ассемблер будет считать вторую команду продолжением комментария (если Вы исправите предыдущий пункт, заменив '';' на '#'). Перед каждой ассемблерной инструкцией (кроме первой) надо добавлять перевод строки и отступ (обычно ставят табуляцию), чтобы каждая новая инструкция была в новой строке.

3. Нумерация параметров ассемблерной вставки ведется с нуля, а не с единицы, то есть вместо "%1" и "%2" должно быть "%0 и "%1" соответственно.

4. В списке "испорченных" регистров указывается только имя регистра, без символа процентов.

5. Совершенно необязательно (часто даже вредно) выполнять загрузку данных в регистры в ассемблерной вставке. Лучше доверить это компилятору.

6. Адрес порта команд out, насколько я знаю, 16-битный, а не 32-битный.

7. По-моему, вывод 8-битных данных выполняется командой outb, а не out (я ассемблер i386 практически не знаю, так что проверяйте сами).

8. Адрес порта должен загружаться не в любой регистр, а непременно в dx.

9. Перепутаны аргументы команды out.

Вот, как мне кажется, будет более правильно:

#include <stdint.h>
void outportb(uint16_t addr, uint8_t data)
{
    asm("outb %0, %1" :: "a"(data), "d"(addr));
}

Результат компиляции:

outportb:
        pushl   %ebp
        movl    %esp, %ebp
        movl    8(%ebp), %edx
        movb    12(%ebp), %al
#APP
        outb %al, %dx
#NO_APP
        leave
        ret

3) Тип void тоже почему-то не понимает... Придется вводить фиктивную выходную переменную типа int, например.
Чтооо??? Это какая версия GCC не понимает тип void? :) Напишите подробнее, что за проблема с void.
Изменено пользователем alx2

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

alx2, спасибо за подсказку по передаче параметров (!!очевидная и простая!!), ассемблеру и за include-файл. С void eщё раз попробую.

Outb нету, ассемблер сам определяет по длине используемого регистра

 

 

Функция void со строкой "asm("out %0, %1" :: "a"(data), "d"(addr));" транслируется, но не работает... Похоже надо ещё защищенный режим у Пентиума снимать... Разберемся.

 

Возможно завтра задам ещё один вопросик. Нужно будет дописать сохранение/восстановление регистров для функции обработки прерывания. Или мы - опять чайники, и где-то в include есть модификатор interrupt?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Фрагмент кода avreal

компилируется (и работает ;) ) gcc - linux, freebsd, mingw32

base - это приватное поле класса, базовый адрес LPT

*_offset - enum-ы на разрешённые для операции регистры LPT

uint8_t
lpt_io_t::read(rd_offset offs) const
{
    uint8_t value;
    __asm__ __volatile__(
        "inb %w1, %0"
        : "=a"(value)
        : "Nd"(base_ + offs)
    );
    return value;
}

void
lpt_io_t::write(wr_offset offs, uint8_t value) const
{
    __asm__ __volatile__(
        "outb %b0, %w1"
        :
        : "a"(value), "Nd"(base_ + offs)
    );
}

void
lpt_io_t::write_data(const uint8_t * buf, int len) const
{
    __asm__ __volatile__(
        "cld" "\n\t"
        "rep outsb"
        : "+S"(buf), "+c"(len)
        : "d"(base_)
    );
}

Для нескольких команд команды надо разделять "\n\t", так как и без '\' всё равно всё сольётся в одну длинную строку средствами С-компилятора ещё до передачи в обработку __asm__

Для in/out адрес должен быть именно в dx - в словном, поэтому казывается %w1 а не %1

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Проблема, что запуск из-под Windows XP. Так что немножко времени необходимо для чтения про корректность постановки задачи. Хотя ключей для запуска приложения в NetBeans штук 5 (тоже надо бы разобраться!).

 

 

...

 

Нет, оказывается из-за запуска из под NetBeans. Нет никакого защищенного режима. При трансляции Минималистом в командной строке и запуска оттуда же всё работает.

Пробую варианты...

 

Спасибо всем!

 

А что означают буковки "a" и "Nd"? :blush: У нас нет никакой литературы по gcc, кроме Артура Гриффитса "GCC. Настольная книга..."

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А что означают буковки "a" и "Nd"? :blush: У нас нет никакой литературы по gcc, кроме Артура Гриффитса "GCC. Настольная книга..."

 

Это называеться "Operand Constraints", описание можно найти здесь:

http://gcc.gnu.org/onlinedocs/gccint/Const...tml#Constraints

 

Анатолий.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А что означают буковки "a" и "Nd"? :blush: У нас нет никакой литературы по gcc, кроме Артура Гриффитса "GCC. Настольная книга..."

 

http://gcc.gnu.org/onlinedocs/gcc/Constrai...tml#Constraints

 

a - это для данного аргумента годится только eax

%b0 - это от нулевого аргумента нас интересует байт, т.е. %al

 

Nd - это для данного аргумента подходит 8-битная константа либо регистр edx

 

uint16_t base;

inline uint8_t
read(unsigned addr)
{
    uint8_t value;
    __asm__ __volatile__(
        "inb %w1, %0"
        : "=a"(value)
        : "Nd"(addr)
    );
    return value;
}

uint8_t a, b;

void test(void)
{
    a = read(0xB0);   // тут будет непосредственный адрес
    b = read(base);    // тут в DX загрузится значение переменной
    a = read(0x3B0);  // тут в DX загрузится константа, которая не лезет в 8 бит
}

gcc -O2 -fomit-frame-pointer -S

_test:
    inb $176, %al                     <--- это от   a = read(0xB0);
    movzwl    _base, %edx      <--- это от   b = read(base);
    movb    %al, _a              <--- это от   a = read(0xB0);
    inb %dx, %al                      <--- это от   b = read(base);
    movl    $944, %edx                <--- это от   a = read(0x3B0);
    movb    %al, _b              <--- это от   b = read(base);
    inb %dx, %al                      <--- это от   a = read(0x3B0);
    movb    %al, _a              <--- это от   a = read(0x3B0);
    ret

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо всем!

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

 

За указание на сайт http://gcc.gnu.org/ - отдельная благодарность.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

в защищенном режиме out и in являются привелегированными и запрнщаются к исполнению пользовательским кодом. Либо в Досе, либо исключительно через драйвер. В драйвере который испольняется в режиме ядра ОС эти команды только и должны использоватся для непосредственного дергания за зелезячки.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Klen, Вы в этом абсолютно уверены? Защищенный режим Пентиума? А как же аппаратный сброс и возврат в реальный режим с использованием команды out? Или я опять чего-то путаю?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...