Ну и там идея какая. Делаем на ассемблере подготовку (переход в защищеный режим и пр.) — а потом уже переходим на уровень выше и пишем на голом С. ОК вроде.
Т.е. все остальное — обращение к видео-памяти и пр. — все это делается на голом С. Видео-память просто через указатель с константным адресом 0xb8000 — тут все ОК. А вот прерывания работу с портами ввода-вывода никак не сделать на голом С — эта концепция как бы не вписывается в язык.
Т.е. если сделать мин. загрузчик и передачу в библиотеку на голом С — то эта библиотека целиком и полностью может обойтись без ассемблерных вставок, окромя вызова прерываний работы с портами ввода-вывода?
По идее порты ввода-вывода такая же базовая концепция как и память? так ведь? Они на уровне железа как и память, верно? Так почему же их не добавили а память добавили?
Прав ли я? Так же интересует мнение по этому поводу.
Здравствуйте, Shmj, Вы писали:
S>Т.е. все остальное — обращение к видео-памяти и пр. — все это делается на голом С. Видео-память просто через указатель с константным адресом 0xb8000 — тут все ОК. А вот прерывания никак не сделать на голом С — эта концепция как бы не вписывается в язык.
В досовские времена были раcширения Borland C++ — отдельное соглашение о вызовах что-то вроде interrupt.
Такие процедуры автоматом сохраняли и восстанавливали регистры.
На современном gcc наверняка есть расширения для системных целей, ибо gcc для юниксов и был изначально написан.
Тем более, для защищенного режима сохранение и восстановление регистров не нужно. Насколько помню, там автоматически происходит переключение задачи на обработчик прерывания. (Знатоки поправят).
Здравствуйте, Shmj, Вы писали:
S>Т.е. если сделать мин. загрузчик и передачу в библиотеку на голом С — то эта библиотека целиком и полностью может обойтись без ассемблерных вставок, окромя вызова прерываний? Ведь прерывания то не мапятся на адрес с возможность дернуть указатель на функцию. S>Прав ли я? Так же интересует мнение по этому поводу.
Ну если есть желание обойтись почти без ассебмлера,
то можно теоретически подсократить в этом данном проекте использование
ассемблера с помощью чего-то типа "__attribute__((__interrupt__))".
Но насколько это нужно не очень понятно, все равно в настоящей ОС
без ассемлера не обойтись.
то даже банальное вычисление количества "замыкающих" нулевых битов
может потребоваться сделать в ОС очень быстро, в x86 есть инструкция "bsf",
которая сделает то что нужно, а без всяких расширений конкретного компилятора типа "__builtin_ctz" (gcc, clang)
фиг ты до него доберешься.
Чего (идеологически важного) нет в С?
Возможности ограничивать и контролировать ресурсы. Например вы не можете попросить выполняться функцию не более 10мс и не хавать более 25кб. Или например вызывать метод не реже чем через n тактов.
Нет локальный функций, нет анонимных функций. Всего один стэк нет возможности возможности оперировать с алгоритмом для его последовательной модификации.
Здравствуйте, Shmj, Вы писали:
S>А вот прерывания никак не сделать на голом С — эта концепция как бы не вписывается в язык.
боже мой, ну ты это всё на серьёзных щах что ли?
в кросс-платформенный язык "как бы не вписывается концепция" аппаратных особенностей конкретных платформ, вот это поворот!
вот это неожиданность, ну кто бы мог такое представить!
Здравствуйте, LuciferSaratov, Вы писали:
LS>боже мой, ну ты это всё на серьёзных щах что ли? LS>в кросс-платформенный язык "как бы не вписывается концепция" аппаратных особенностей конкретных платформ, вот это поворот! LS>вот это неожиданность, ну кто бы мог такое представить!
Подправил — там же не прерывания а работа с портами ввода-вывода, это такая же базовая концепция железа как и память. Но для работы с памятью есть механизм почему то а для портов нету
Но тут как посмотреть, конечно, ведь некоторые как бы мапят и порты а память.
Видимо так же и прерывания можно мапить на память в виде указателей на функцию.
Кстати, на каком уровне произведен мапинг видеопамяти как бы в обычную память? Это на уровне BIOS или на уровне железа?
Здравствуйте, rg45, Вы писали:
S>>Чего (идеологически важного) нет в С? R>RAII
Да опять вы за свое. Я не об этом.
Вопрос в другом. Вот есть голое железо и язык С. Почему мы не можем полностью работать с этим железом на голом С а нужны некие ассемблерные вставки? По каким причинам?
Вот одно из что я привел — нет поддержки портов ввода-вывода как в примере выше.
Ведь с памятью то можем работать же напрямую. А с портами нет. Что еще кроме памяти и портов нужно для железа? И кто мапит память устройств на общую память — это на уровне железа или на уровне BIOS?
Здравствуйте, Shmj, Вы писали:
S>По идее порты ввода-вывода такая же базовая концепция как и память? так ведь? Они на уровне железа как и память, верно? Так почему же их не добавили а память добавили?
У Интеловских процессоров, начиная с 8086 (а, вернее, с 8080) порты не отображены на память. Чтение из порта и чтение из памяти — это разные операции на шине. Поэтому у них и команды разные.
У большинства других процессоров порты ввода-вывода, действительно, отображены на диапазон адресов памяти и с ними работают теми же командами.
Но это еще и от перефирийного устройства зависит. Среди PCI-ных устройств совершенно не редкость, когда то, что логически является портами ввода-вывода, фактически отображено на пространство памяти.
Здравствуйте, Shmj, Вы писали:
S>Вопрос в другом. Вот есть голое железо и язык С. Почему мы не можем полностью работать с этим железом на голом С а нужны некие ассемблерные вставки? По каким причинам?
Железа разного много, а си один. А асм под каждую архитектуру свой.
Здравствуйте, Shmj, Вы писали:
S>По идее порты ввода-вывода такая же базовая концепция как и память? так ведь? Они на уровне железа как и память, верно? Так почему же их не добавили а память добавили?
Как уже сказали выше, отдельное адресное пространство портов ввода-вывода — это специфика x86, на других архитектурах это может быть обычная память.
В Borland С++ были соответствующие интринсики.
Я помню, в студенческие годы изобретал велосипед с оператором [], чтобы пользоваться как в турбо-паскале
port[i] = j;
В С вообще нет ничего для работы с железом. Я не знаю, почему ты думаешь, что в С можно работать с памятью. В общем случае нельзя. Если и получается, то это особенность компилятора (то бишь это уже не совсем C и не переносимо, впрочем это и очевидно).
Что такое порты, я не знаю. Я работал немного с микроконтролерами, там нет никаких портов, взаимодействие с оборудованием осуществляется через память. Поэтому утверждать, что эти порты это базовая концепция, я бы не стал.
Здравствуйте, vsb, Вы писали:
vsb>В С вообще нет ничего для работы с железом. Я не знаю, почему ты думаешь, что в С можно работать с памятью. В общем случае нельзя. Если и получается, то это особенность компилятора (то бишь это уже не совсем C и не переносимо, впрочем это и очевидно).
Вот этот момент мне и не ясен — кто и почему решил, что адрес 0xb8000 — это именно видеопамять при начальной загрузке (в режиме VGA или как там)? Наверное даже не компилятор а BIOS-система это определяет, скорее всего. Или нет...
Вот если без BIOS загрузиться — как обратиться к девайсам?
vsb>Что такое порты, я не знаю.
Порт — это и есть та самая ножка микроконтроллера, к которой ты припаиваешь светодиод. Ну да, обычно оно тоже как-то мапится на память.
И тоже тут вопрос — на каком уровне оно мапится на память? На уровне компилятора или девайса?
Здравствуйте, Shmj, Вы писали:
S>Подправил — там же не прерывания а работа с портами ввода-вывода, это такая же базовая концепция железа как и память. Но для работы с памятью есть механизм почему то а для портов нету
1. Порты как механизм специфичны только для x86 линии (начиная с 8080) и в таком виде появились от того, что методы обращения к памяти и IO предполагались разные, а сводить в одну команду авторам 8080 не хотелось. Дополнительные линии на шине помогали сократить сложность опознания обращения.
Обрати внимание, что в ранних IBM PC делали, например, что порты 44-47, 48-4B, 4C-4F, ..., 5C-5F были тем же самым что 40-43. Это чисто экономия на декодинге. Потом уже начали их использовать. Опять же, чем дешевле для первой версии, тем лучше.
Остальные архитектуры использовали отображение на память. Ну не считаем линию от S/360 до System/Z, в которой только пара базовых устройств типа таймера доступна через встроенные команды, а остальные — через каналы ввода-вывода (в качестве аналогии представь себе, что это всё висит на таком ну очень быстром USB или SCSI, что легче представить).
Так что ничего "базового" в портах в их нынешнем x86 виде нет, это локальная заморочка.
2. В большинстве Unix-систем доступ к портам идёт через функции, часто заврапленные под конкретные шинные переходники. Вот, например, сетевуха системы "старой доброй" NE2000 может отразить регистры на память, а может на порты. Драйвер пользуется универсальным bus_write_i32(), а вызвать команду out или запись в память — решает нижележащий драйвер шины.
А вот когда уже дошло до конкретной in или out, используются ассемблерные переходники или builtin-функции. Компилятор может их заинлайнить (скорее всего), а может — нет, по настроению. В любом случае платформа в виде компилятора даёт такое средство.
3. С прерываниями таки важнее — функция, пригодная напрямую из C как обработчик прерывания, должна иметь особые пролог и эпилог. Заморачиваются этим только в мелком embedded. В крупном (грубо говоря, где можно запустить Linux) используют ассемблерные переходники.
S>Но тут как посмотреть, конечно, ведь некоторые как бы мапят и порты а память.
Мапят, да. Могут вообще пространство I/O не использовать.
Если говорить об x86, есть ещё третье адресное пространство — конфигурации PCI. Ты о нём даже не пытался вспоминать а ведь самые базовые настройки железа сейчас собраны именно туда. Оно в зависимости от чипсета накладывается или на участок памяти, или на порты ввода-вывода в режиме узкого окна.
S>Видимо так же и прерывания можно мапить на память в виде указателей на функцию.
Нет. Для C нужны пролог, который создаст всю необходимую обстановку для C рантайма, и эпилог, который восстановит всё обратно.
S>Кстати, на каком уровне произведен мапинг видеопамяти как бы в обычную память? Это на уровне BIOS или на уровне железа?
Судя по формулировке вопроса, ты не понимаешь, что спрашиваешь.
Непосредственно обращения отрабатывает железо. А вот правила раутинга запросов доступа для него настраивает или BIOS, или потом ОС.
Здравствуйте, Shmj, Вы писали:
S>Вопрос в другом. Вот есть голое железо и язык С. Почему мы не можем полностью работать с этим железом на голом С а нужны некие ассемблерные вставки? По каким причинам?
Для настройки среды, в которой работает C код, нужно как минимум настроить:
1. Стек — выделить память, поставить указатель на её границу.
2. Для архитектур где используется global pointer (такой регистр) — его.
Это нельзя сделать сишным кодом. Настройка идёт откуда-то извне. Стандартно и удобнее всего — ассемблер.
Далее, в обработчике прерывания надо на входе сделать то же самое (в плане установки регистров) и сохранить все регистры, которые не сохраняются в обычной функции (через них передаются аргументы и забирается результат), на выходе — восстановить их.
S>Вот одно из что я привел — нет поддержки портов ввода-вывода как в примере выше.
Не смотри на x86. Смотри на ARM или MIPS. Там портов нет, с ними не надо работать.
А вот то что я выше назвал — надо.
А ещё там разные барьеры памяти, которые может не уметь компилятор сам по себе.
А ещё специфичные команды типа WFE (wait for events).
S>Ведь с памятью то можем работать же напрямую. А с портами нет. Что еще кроме памяти и портов нужно для железа? И кто мапит память устройств на общую память — это на уровне железа или на уровне BIOS?
В ARM/MIPS/etc. обычно жёстко прописано в железе.
В x86 всё сложно. Если ME не выделит заранее BIOS'у оперативную память, то и её нет! Надо сначала вытащить данные конфигурации модулей памяти и настроить маппинг через регистры.
Регистры эти в PCI configuration space, куда, пока не настроил их отображение на память, доступ только через шлюз в виде двух регистров ввода-вывода (CF8, CFC).
Начальный этап BIOS, пока она это настраивает, нельзя написать на C, потому что там даже стека нет. Потом может использовать кэш процессора как эмуляцию памяти, но он маленький. Всё равно ассемблер.
Только после этого можно подключать основную память.
EFI где-то так и сделан — после первой инициализации строится среда для кода, написанного в основном на C/C++, и он начинает выполняться.
Здравствуйте, Shmj, Вы писали:
vsb>>В С вообще нет ничего для работы с железом. Я не знаю, почему ты думаешь, что в С можно работать с памятью. В общем случае нельзя. Если и получается, то это особенность компилятора (то бишь это уже не совсем C и не переносимо, впрочем это и очевидно).
S>Вот этот момент мне и не ясен — кто и почему решил, что адрес 0xb8000 — это именно видеопамять при начальной загрузке (в режиме VGA или как там)? Наверное даже не компилятор а BIOS-система это определяет, скорее всего. Или нет...
Интересно ты смешиваешь разные вещи — одинокий порт на одной ножке для микроконтроллера и адреса видеопамяти для x86. Ты понимаешь, что между ними разница в несколько уровней?
S>Вот если без BIOS загрузиться — как обратиться к девайсам?
К одним просто — они сидят на стандартных адресах в пространстве портов.
К другим сложнее — надо сходить узнать их конфигурацию и настроить, какие адреса будут использоваться, а перед этим — настроить ещё и шлюзы между шинами.
vsb>>Что такое порты, я не знаю. S>Порт — это и есть та самая ножка микроконтроллера, к которой ты припаиваешь светодиод. Ну да, обычно оно тоже как-то мапится на память.
"Это не те порты, которые ты ищешь" (tm).
S>И тоже тут вопрос — на каком уровне оно мапится на память? На уровне компилятора или девайса?