Здравствуйте, andyp, Вы писали:
vsb>>Вроде общепринятая точка зрения, что C ближе всего к железу, ну если не считать языков ассемблера.
A>И ты ее разделяешь что ли? С — абстрактная машина, описанная в стандарте. Много какое железо плохо с этой абстрактной машиной согласуется. На нем С близким к железу быть перестает.
Я не эмбедщик, мой потолок это попсовый ARM, так что про такое железо (полагаю, речь про DSP или вообще про видеокарты и подобное) я только в теории слышал.
А так — хз, я уже писал, мне С не нравится в текущей инкарнации, мне было бы комфортней программировать на чём-то ближе к ассемблеру. Есть такое ощущение, что раньше были всякие макро-ассемблеры, на них можно было много чего интересного слепить на этих макросах, возможно мне этого и хочется, как-нибудь поисследую эту тему. Хотя допускаю, что я сам не знаю, чего я хочу.
Здравствуйте, vsb, Вы писали:
vsb>мне С не нравится в текущей инкарнации, мне было бы комфортней программировать на чём-то ближе к ассемблеру.
Если нужно один раз по-быстрому накидать что-нибудь мелкое и примитивное, а на C/C++ получается жирно и/или медленно, и лень возиться с оптимизацией — не вопрос. Но уже где-то с тысячи-другой строк на ассемблере C/C++ становится однозначно выгоднее. Время, затраченное на изучение особенностей кодогенерации и работы CRT, компенсируется ускорением разработки последующих проектов.
vsb>Есть такое ощущение, что раньше были всякие макро-ассемблеры, на них можно было много чего интересного слепить на этих макросах
Можно, но все развитые ассемблеры были заточены в первую очередь под имитацию ЯВУ — процедуры, автоматизация выделения памяти в стеке под локальные переменные, автоматизация складывания парамеров в стек перед вызовом, и т.п. То есть, как только такая программа становится достаточно большой, сразу же возникает вопрос — почему она делается на ассемблере, а не на ЯВУ.
Я в конце 80-х делал довольно сложный прикладной софт на PDP-11 под RSX-11M на ассемблере — просто потому, что компилятор C там был примитивный и очень медленный, а на ассемблере я к тому времени наделал много разных драйверов ядра, он мне был привычен, и у меня была наработанная библиотека типовых процедур для него. Будь там компилятор побыстрее и поприличнее (хотя бы ANSI C) — делал бы на нем. У того же Turbo C 2.0 скорость и кодогенерация были просто отличными для того времени.
vsb>допускаю, что я сам не знаю, чего я хочу.
Здравствуйте, vsb, Вы писали:
vsb>А так — хз, я уже писал, мне С не нравится в текущей инкарнации, мне было бы комфортней программировать на чём-то ближе к ассемблеру. Есть такое ощущение, что раньше были всякие макро-ассемблеры, на них можно было много чего интересного слепить на этих макросах, возможно мне этого и хочется, как-нибудь поисследую эту тему. Хотя допускаю, что я сам не знаю, чего я хочу.
Это тебе кажется, что было бы конфортней. Я участвовал в проекте на макроассемблере под миллион строк. Каждая строка асма была откомментирована, были макросы на вход-выход в функции. Но все равно, ты вынужден писать много кода, требующего постоянной концентрации, даже когда привыкнешь, постоянное жонглирование регистрами мрачно достает. Я искал одну ошибку два месяца — в обработчике прерываний не сохранялся один из использованных регистров. И что думаешь, я был рад, когда ее нашел? Да я тупо проклинал того пидара, который её посадил — два месяца из-за него железка не проходила один грёбаный тест — аудио изредка хрюкало.
Здравствуйте, andyp, Вы писали:
A>в обработчике прерываний не сохранялся один из использованных регистров.
Такое хоть можно найти глазами. А вот когда ошибся в адресной арифметике и писанул не туда... В C/C++ это тоже нетрудно, но там хоть можно натыкать assert'ов — где угодно и каких угодно, а в ассемблере их так же тупо не вставишь.
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Такое хоть можно найти глазами. А вот когда ошибся в адресной арифметике и писанул не туда... В C/C++ это тоже нетрудно, но там хоть можно натыкать assert'ов — где угодно и каких угодно, а в ассемблере их так же тупо не вставишь.
Видишь ли, это был цифровой приемник с горой ЦОСа, выполняющегося на трёх частотах дискретизации. Я шел от входа, а прерывание было в конце, где готовое аудио из циркулярного буфера уже отдавалось в другую железку А в С это тоже не трудно:
Здравствуйте, 777777w, Вы писали:
ЕМ>>Это далеко не всегда настолько сложно, как может показаться. Атаки всегда проводятся адресно
7>Что значит "атака"? Программа здесь совершенно пассивна, если повезёт — то ей будут доступны данные каких-то программ (и она даже не знает каких именно), а не повезёт — будет пустая память.
Атака — при том, что эта программа будет ждать данных каких-то конкретных программ, пытаться отыскать их выданной ей памяти. Это и называется "атака на <ПОДСТАВИТЬ_ИМЯ_АТАКУЕМОЙ_ПРОГРАММЫ>"
Здравствуйте, vsb, Вы писали:
vsb>А так — хз, я уже писал, мне С не нравится в текущей инкарнации, мне было бы комфортней программировать на чём-то ближе к ассемблеру.
Не нравится C — пиши на асме. Только думаю, после этого тебе C начнёт нравится гораздо больше
vsb>Есть такое ощущение, что раньше были всякие макро-ассемблеры, на них можно было много чего интересного слепить на этих макросах, возможно мне этого и хочется, как-нибудь поисследую эту тему. Хотя допускаю, что я сам не знаю, чего я хочу.
Были, и, полагаю, никуда не делись. Да и вообще, накидать свой макропроцессор поверх асма — дело нескольких дней. Или можно любой готовый взять, m4 например. Просто в make файле доп правило будет, и всё
Здравствуйте, Marty, Вы писали:
M>Миллион строк на асме — это жестяк
Там было 5 DSP общающихся между собой и с миром по многоканальным последовательным портам и уарту. В новой плате DSP стало два, но более быстрых. В результате перетаскивания на новое железо и накосячили — все вроде перетащили, а оно почему-то не завелось . Большей кучи дерьма я в жизни после этого не видел, та что ну его нафиг, этот сплошной асм. Пусть лучше C даже с кривым компилятором, если есть возможность воткнуть RTOSку — надо втыкать. Меньше пишешь — больше шансов дописать и увидеть как заработает
Здравствуйте, andyp, Вы писали:
M>>Миллион строк на асме — это жестяк
A>Там было 5 DSP общающихся между собой и с миром по многоканальным последовательным портам и уарту. В новой плате DSP стало два, но более быстрых. В результате перетаскивания на новое железо и накосячили — все вроде перетащили, а оно почему-то не завелось . Большей кучи дерьма я в жизни после этого не видел, та что ну его нафиг, этот сплошной асм. Пусть лучше C даже с кривым компилятором, если есть возможность воткнуть RTOSку — надо втыкать. Меньше пишешь — больше шансов дописать и увидеть как заработает
Здравствуйте, Marty, Вы писали:
vsb>>Есть такое ощущение, что раньше были всякие макро-ассемблеры, на них можно было много чего интересного слепить на этих макросах, возможно мне этого и хочется, как-нибудь поисследую эту тему. Хотя допускаю, что я сам не знаю, чего я хочу.
M>Были, и, полагаю, никуда не делись. Да и вообще, накидать свой макропроцессор поверх асма — дело нескольких дней. Или можно любой готовый взять, m4 например. Просто в make файле доп правило будет, и всё
Я ещё щас думаю, что мне не хватает аналога async/await в С. Чтобы я мог записать алгоритм в простом виде, а компилятор из этого сварганил машину состояний и оно бы работало параллельно с другими такими же машинами состояний. Я что-то подобное сейчас руками делаю, но осознавая, насколько это проще могло бы быть, немного печалюсь.
Вроде в Rust оно как-то так работает, но он сложный...
Здравствуйте, vsb, Вы писали:
vsb>не хватает аналога async/await в С. Чтобы я мог записать алгоритм в простом виде, а компилятор из этого сварганил машину состояний и оно бы работало параллельно с другими такими же машинами состояний.
Такое может неплохо работать на мощных процессорах, когда запас по быстродействию большой. На МК это рискует тормозить, причем непредсказуемо.
vsb>Я что-то подобное сейчас руками делаю
Делаете с пониманием того, как работает вся эта кухня, или интуитивно?
vsb>Вроде в Rust оно как-то так работает, но он сложный...
Здравствуйте, Евгений Музыченко, Вы писали:
vsb>>не хватает аналога async/await в С. Чтобы я мог записать алгоритм в простом виде, а компилятор из этого сварганил машину состояний и оно бы работало параллельно с другими такими же машинами состояний.
ЕМ>Такое может неплохо работать на мощных процессорах, когда запас по быстродействию большой. На МК это рискует тормозить, причем непредсказуемо.
Не понимаю, о чём речь.
Грубо говоря, сейчас алгоритм такой:
for (size_t i = 0; i < length; i++) {
while (TBE == 0) {} // ждем пока не включится флаг TRANSMIT_BUFFER_EMPTY
TRANSMIT_DATA := transmit_data[i] // записываем первый байт
while (RBNE == 0) {} // ждём пока не включится флаг RECEIVE_BUFFER_NOT_EMPTY
receive_data[i] := RECEIVE_DATA;
}
это блокирующий алгоритм. Если хочется его делать не в блокирующем режиме, то этот алгоритм переводится в машину состояний вроде
void process_events() {
switch (state) {
case WAIT_FOR_TBE:
if (TBE != 0) state = TRANSMIT_BYTE;
break;
case TRANSMIT_BYTE:
TRANSMIT_DATA := transmit_data[i];
state = WAIT_FOR_RBNE;
break;
case WAIT_FOR_RBNE:
if (RBNE != 0) state = RECEIVE_BYTE;
break;
case RECEIVE_BYTE:
receive_data[i] := RECEIVE_DATA;
state = NEXT_ITERATION;
break;
case NEXT_ITERATION:
i++;
if (i < length) {
state = WAIT_FOR_TBE;
} else {
state = END;
}
break;
}
}
Как видно, блокирующий алгоритм переделали в неблокирующий, теперь надо только достаточно часто вызывать process_events();
но переделка абсолютно механическая и по этому алгоритму работают многие языки, реализуя async/await под капотом именно в таком виде.
Т.е. можно было бы взять начальный код
for (size_t i = 0; i < length; i++) {
while (TBE == 0) yield; // ждем пока не включится флаг TRANSMIT_BUFFER_EMPTY
TRANSMIT_DATA := transmit_data[i] // записываем первый байт
while (RBNE == 0) yield; // ждём пока не включится флаг RECEIVE_BUFFER_NOT_EMPTY
receive_data[i] := RECEIVE_DATA;
}
И по yield разбить функцию на куски и каждый кусок оформить в виде куска в switch машины состояний. Ну тут нет вызовов других функций, в общем случае всё: конечно, может быть похитрей, но суть та же.
Понятно, что можно вкорячить туда какой-нибудь FreeRTOS и запускать такие алгоритмы, как полновесные процессы, но это кажется ненужным усложнением.
vsb>>Я что-то подобное сейчас руками делаю
ЕМ>Делаете с пониманием того, как работает вся эта кухня, или интуитивно?
Не очень понял вопроса. Понимать там нечего, всё примитивно.
vsb>>Вроде в Rust оно как-то так работает, но он сложный...
ЕМ>А есть реализации Rust под требуемые МК?
Под интересующие меня — конечно есть, там же LLVM, он много чего умеет. Впрочем я не пробовал, Rust я и сам знаю плохо, и код я пишу не для себя, такой код другие поддерживать не смогут. Считаю, что нужно придерживаться индустриальных стандартов, коим Rust пока не является.
V>Модифицированная — это с мультиплексированием шины. V>(для программиста эта особенность не заметна)
Это смотря куда модифицированная — бывает еще модифицированная в сторону двух шин данных и одной шины программ, как бывало у Моторолы. Ну и есть еще куча промежуточных модификаций гарварда, по сути позволяющих подтягивать два операнда данных за такт — например, если исполняемый код в это время берется из некоего кэша. Одну из таких модификаций контора Analog Devices скромно называла супергарвардом.
Здравствуйте, vsb, Вы писали:
vsb>Я ещё щас думаю, что мне не хватает аналога async/await в С. Чтобы я мог записать алгоритм в простом виде, а компилятор из этого сварганил машину состояний и оно бы работало параллельно с другими такими же машинами состояний. Я что-то подобное сейчас руками делаю, но осознавая, насколько это проще могло бы быть, немного печалюсь.
Здравствуйте, vsb, Вы писали:
vsb>алгоритм переводится в машину состояний
По-русски это "конечный автомат".
vsb>по этому алгоритму работают многие языки, реализуя async/await под капотом именно в таком виде
Там, где последовательный алгоритм разбивается на отдельные функции, границами которых являются операции ожидания, получается примерно то же, что и switch, только каждый case вынесен в свою функцию. Чтобы вызывать их в нужные моменты, реализация языка поддерживает "под капотом" очереди ожидания и события. Для алгоритма с десятком состояний это вряд ли оправдано.
Вообще, любой конечный автомат естественным образом ложится на объект (в предельном случае — структуру), где хранится состояние, и набор методов/функций вокруг него.
ЕМ>>Делаете с пониманием того, как работает вся эта кухня, или интуитивно?
vsb>Понимать там нечего, всё примитивно.
Если хотите, чтоб код оставался линейным, и паузы по вызову yield делались внутри функции, то не обойтись без переключения (или копирования/восстановления) стека. Для этого нужно знать, как реализация языка работает со стеком. Тут без ассемблерной обвязки вряд ли можно обойтись.
Здравствуйте, Marty, Вы писали:
M>Зачем руками, когда есть PROTOTHREADS?
Оно ж совсем невнятное. Что толку от имитации последовательной работы, когда значения переменных не сохраняются? Такое годится для тех, кто в параллельной обработке вообще ничего не смыслит, чтоб могли тупо записать простенький последовательный алгоритм.
Я б сделал объекты/структуры, хранящие состояния процессов, функции-обработчики событий, и главный диспетчер, обнаруживающий события и вызывающий обработчики.