Если вам нужно использовать -masm=intel
, вам нужно убедиться, что встроенная сборка соответствует синтаксису Intel.Синтаксис Intel - dst, src (синтаксис AT & T обратный).Этот несколько связанный ответ содержит некоторую полезную информацию о некоторых различиях между вариантом Intel NASM 1 (не вариант GAS) и синтаксисом AT & T:
Информация о том, как выможно перейти к переводу синтаксиса NASM Intel в синтаксис AT & T GAS, который можно найти в этом ответе Stackoverflow , и много полезной информации предоставлено в этой статье IBM .
[snip]
Как правило, самые большие различия:
- С синтаксисом AT & T источник находится слева, а пункт назначения - справа, а Intel - наоборот.
- В синтаксисе AT & T к именам регистров добавляется
%
- В синтаксисе AT & T к немедленным значениям добавляется
$
- Операнды памяти, вероятно, являются самой большой разницей.NASM использует [сегмент: disp + base + index * scale] вместо синтаксиса GAS в сегмент: disp (base, index, scale) .
Проблема в вашем коде заключается в том, что исходные и целевые операнды должны быть обращены от исходного синтаксиса AT & T, с которым вы работали.Этот код:
asm volatile ( "inb %1, %0"
: "=a"(ret)
: "Nd"(port) );
Должен быть:
asm volatile ( "inb %0, %1"
: "=a"(ret)
: "Nd"(port) );
Что касается вашего обновления: проблема в том, что в синтаксисе Intel к непосредственным значениям не добавляется $
.Эта строка является проблемой:
asm volatile ( "outb $0x80, %0" : : "a"(0) );
Это должно быть:
asm volatile ( "outb 0x80, %0" : : "a"(0) );
Если бы у вас была правильная функция outb
, вы могли бы сделать что-то подобное вместо этого:
#include <stdint.h>
#include "ioaccess.h"
uint8_t inb(uint16_t port)
{
uint8_t ret;
asm volatile ( "inb %0, %1"
: "=a"(ret)
: "Nd"(port) );
return ret;
}
void outb(uint16_t port, uint8_t byte)
{
asm volatile ( "outb %1, %0"
:
: "a"(byte),
"Nd"(port) );
}
void io_wait(void)
{
outb (0x80, 0);
}
Немного более сложная версия, поддерживающая диалекты AT & T и Intel :
Несколько диалектов на ассемблере в шаблонах asm На таких объектах, как x86, GCC поддерживаетнесколько диалектов ассемблера.Опция -masm определяет, какой диалект GCC используется по умолчанию для встроенного ассемблера.Специфичная для цели документация для опции -masm содержит список поддерживаемых диалектов, а также диалект по умолчанию, если опция не указана.Эта информация может быть важна для понимания, поскольку ассемблерный код, который работает правильно при компиляции с использованием одного диалекта, скорее всего потерпит неудачу, если скомпилируется с использованием другого.См. Параметры x86.
Если ваш код должен поддерживать несколько диалектов ассемблера (например, если вы пишете публичные заголовки, которые должны поддерживать различные параметры компиляции), используйте конструкции этой формы:
{dialect0 |dialect1 |dialect2 ...}
Для целей x86 и x86-64 есть два диалекта.Dialect0 - это синтаксис AT & T, а Dialect1 - это синтаксис Intel.Функции могут быть переработаны следующим образом:
#include <stdint.h>
#include "ioaccess.h"
uint8_t inb(uint16_t port)
{
uint8_t ret;
asm volatile ( "inb {%[port], %[retreg] | %[retreg], %[port]}"
: [retreg]"=a"(ret)
: [port]"Nd"(port) );
return ret;
}
void outb(uint16_t port, uint8_t byte)
{
asm volatile ( "outb {%[byte], %[port] | %[port], %[byte]}"
:
: [byte]"a"(byte),
[port]"Nd"(port) );
}
void io_wait(void)
{
outb (0x80, 0);
}
Я также дал символические имена ограничений вместо использования %0
и %1
, чтобы упростить чтение и обслуживание встроенной сборки. Из GCCВ документации каждое ограничение имеет вид:
[[asmSymbolicName]] ограничение (cvariablename)
Где:
asmSymbolicName
Определяет символическое имя для операнда.Назовите имя в шаблоне ассемблера, заключив его в квадратные скобки (т. Е. «% [Value]»).Область имени - это оператор asm, который содержит определение.Допустимо любое допустимое имя переменной C, включая имена, уже определенные в окружающем коде.Никакие два операнда в одном и том же операторе asm не могут использовать одно и то же символическое имя.
Если не используется asmSymbolicName, используйте позицию операнда (начиная с нуля) в списке операндов в шаблоне ассемблера.Например, если есть три выходных операнда, используйте «% 0» в шаблоне для ссылки на первый, «% 1» для второго и «% 2» для третьего.
Thisверсия должна работать 2 независимо от того, компилируете ли вы с -masm=intel
или -masm=att
опциями
Сноски
- 1 Хотя NASM Intel диалект и GAS (GNU Assembler) синтаксис Intel похожи, есть некоторые различия.Во-первых, в синтаксисе NASM Intel используется [сегмент: disp + base + index * scale] , где сегмент может быть указан внутри
[]
, а для синтаксиса Intel GAS требуется сегмент снаружи с сегментом : [disp + base + index * scale] . - 2 Несмотря на то, что код будет работать, вы должны поместить все эти основные функции непосредственно в файл
ioaccess.h
и удалить их из.c
файл, который содержит их.Поскольку вы поместили эти основные функции в отдельный файл .c
(внешняя связь), компилятор не может оптимизировать их так хорошо, как мог бы.Вы можете изменить функции типа static inline
и поместить их в заголовок напрямую.Компилятор будет тогда иметь возможность оптимизировать код, удаляя служебные вызовы функции и уменьшая потребность в дополнительных нагрузках и хранилищах.Вы захотите скомпилировать с оптимизацией выше -O0
.Рассмотрим -O2
или -O3
. - Особые замечания по разработке ОС :
- Существует множество игрушечных ОС (примеры, учебные пособия и даже код в OSDev Wiki ), которые не работают с оптимизацией.Многие сбои происходят из-за плохой / плохой встроенной сборки или из-за неопределенного поведения.Встроенная сборка должна использоваться в качестве крайней меры.Если ваше ядро не работает с оптимизациями, оно, вероятно, не является ошибкой в компиляторе (это возможно, просто маловероятно).
- Прислушайтесь к совету в ответе @PeterCordes относительно доступа к порту, который может вызвать чтение DMA.