Всем привет! Вопрос - какие сейчас существуют языки системного программирования? Интересно что придумали на замену Си. О ржавом вкурсе. Его можно не упоминать
Если тебе по существу, то системным программированием можно заниматься на любом ЯП, умеющем нативную компиляцию и не вводящем принудительную сборку мусора. А если вопрос «какой ЯП сейчас принято считать системным», то это как раз то, о чём ты просил не упоминать. :)
Странная какая-то тема. Операционная система управляет процессами, памятью и вводом-выводом. Реализовать это можно на любом не слишком специализированном ЯП, на что справедливо указали в комментариях. Поэтому рассуждать о системном уровне несколько странно.
На практике обычно нужна сишечка, чтобы абстрагироваться от каждого конкретного ассемблера, но при этом эффективно ворочать байтами. Как вариант, C++. Поэтому весьма условно системным уровнем можно считать уровень средний.
Так уж сложилось, что Linux написан на C, но недавно притащили еще и Rust, на котором можно написать какой-нибудь модуль ядра. Windows написана на C++. Чтобы присоединиться к состоявшимся проектам, нужно писать на тех же языках.
Если же писать свою систему с нуля, то можно взять что угодно. Для дела - что угодно среднеуровневое. Чисто для фана - да хоть Perl.
OCaml да уже везде вроде засунули, даже в восьмиразрядные процессоры, но хаскель вроде же кроме генерации сишного кода в таком качестве не используется, тупо рантайм не даст. Лисп или схему да тоже можно ужать очень сильно.
В общем-то, никто не мешает таким же макаром сделать поддержку вообще любого восьмибитного контроллера. Благо, у них система команд довольно куцая обычно. Задача уровня курсовой для второкурсника.
знаю :) до 32 в QLC, но для работы онных уровней там намудрено очень много схемотехники.
мудрить подобную схемотехнику на каждую сигнальную линию очень не выгодно, тем более есть более эффективные системы повышения пропускной способонсти сигнальной линии.
потому и предполагаю что тут попутан физический уровень с какойто кодировкой типа хемминга.
Мне что-то кажется, код вот на этом окамле не будет с кодом на обычном окамле иметь почти ничего общего. Т.е. это отдельный диалект с совершенно отдельной практикой написания кода. Я не очень понимаю этот онанизм на добавление поддержки экзотических таргетов в какой-то язык, если почти вся кодовая база этого языка ни разу не применима.
У хачкелла с 6502 можно использовать огромную библиотеку, благо она останется вся только на стадии компиляции, а в бинарик уйдёт довольно маленький выхлоп без необходимости в каком-либо рантайме в принципе.
но у хаскеля это все же не то, понятно что DSL но по сути ближе просто к компилятору-транслятору.
Да нет, это как раз именно то что нужно. Хачкель точно так же в Verilog транслируют, например. Получается весьма офигенно.
Почему сразу «скриптомакака»? По себе судишь? По-моему можно нисколько не любить C++, оставаясь при этом хорошим разработчиком на тех же самых C++. Просто на них работы больше. Где вот ты хорошую работу найдешь на тех же Rust или Haskell? Вот они красивы и по-умному продуманы, а не как «само так получилось», но на C++ работы просто больше.
Смотрел сейчас на hh одну вакансию на Rust. Оказалась крипта… И так многое на Rust. Да еще и иностранщина обычно какая-то
Если тебе по существу, то системным программированием можно заниматься на любом ЯП, умеющем нативную компиляцию и не вводящем принудительную сборку мусора.
При условии что этот любой язык С/С++. Если бы было что-то более подходящее на нём бы во всю писали.
интересно, уперлись в какой-то предел %)
странно, что не применили ddr модуляцию, или сериализацию в lvds, хотя PCIe и так lvds… дробление уровней внутри lvds… надо почитать.
про !ъ:нанотехнологии и выше и ниже по треду (например, про тот же tinygo).
ты его подробно смотрел? что именно там не реализовано? сколько занимает минималистичный рантайм и куда именно оно такое вот недоделанное может поместиться?
SDK для микроконтроллеров итп, тоже в основном сишные.
ну вот тебе, например, для go или .net модное и молодёжное.
там как, можно на Dylan.NET с нанотехнологиями что-нибудь писать или «да, но пока нет»?
я вот не понял этот хайп и пиар про tinygo и nanoframework.
хотя про руст и аду частично понял.
проще всего рантайм отстреливался и заменялся на свою tiny реализацию в аде, ним, D v1.0 том же или схеме ribbit.
я уж не говорю что выше и ниже по треду вполне можно было бы на схеме реализовать асм кодогенерацию, на асме – схему, на схеме – акторную модель какую-нибудь.
а то какое-то странное «системное» программирование получается – с толстым рантаймом и плохоструктурированным концептуально вагоном фич которые не используешь, а платить всё равно надо.
далее читаем например 11-gholum.pdf или от того же автора в кодогенераторе Ikarus scheme: ikarus.intel-assembler.ss, и про схему ribbit и justin.lol sectorlisp
и получаем «ассемблер на лиспе» (ну или лисп на таком ассемблере если подраскрутить метациклически)
если про конечные автоматы – читаем Шалыто про SWITCH-технологию и Легалова про автоматное программирование.
в общем, питон это прежде всего тулинг. например, asciidoc довольно приятный. хотя переписанный на nim был бы компактнее.
а вообще странно видеть питоноподобное в системном программировании – там же глобальные блокировки интепретатора всю дорогу, и рантайм сам по себе довольно толстый.
вот какой-нибудь компилируемый в си недопитон с питонячьим синтаксисом – ещё куда ни шло.
главное, чтобы оно в очередной ч0рный ящик не начало превращаться.
Поэтому рассуждать о системном уровне несколько странно.
ну например: первый вариант.
в аде есть task и task type, task entry и механизм синхронизации через рандеву. синхронизируются task entry и контролируемая память. то есть далее мы получаем понятный вид многозадачности – встроенной в сам язык.
можно написать планировщик, например. который реализует пул объектов. например, берёт access type (корректно типизированную, проверяемую компилятором ссылку) а не address type (обычные сырые сишные указатели) на конкретные tasks, или их элементы. помещаем это в массив, и получаем статически заданный во время компиляции пул объектов.
и запускаем две задачи: одна реализует обновление GUI, другая – планировщик. который запускает каждую задачу из массива.
то есть: компилятор знает о «задачном типе» и контролируемой памяти получаем многозадачность проверяемую компилятором.
не то чтобы там невозможно было сделать deadlock/livelock.
но например, проверить какие-то контракты на SPARK для этих задач.
то есть: на системном уровне – в ада встроена многозадачность в язык и адекватная модульность, программирование по-большому (с родительскими и дочерними пакетами, параметризацией модулей дженериками).
второй вариант. берём BCPL, C, C++. в котором всего этого нет.
и получает нетранзитивность константости – не проверямую компилятором. потому что он на этом уровне рассуждать не умеет – концепции не интегрированы в язык. а UB – интегрировано.
2.5 tiny или обычный go и его встроенную многозадачность на CSP.
третий вариант. берём Dlang и Compile-time metaprogramming и например, immutable а не const. опа, оказывается D умеет – а С++ не умеет.
четвёртый вариант. берём языки с изменяемым синтаксисом и макросами: коммон лисп и его макросы, схему и её модульные гигиеничные макросы, форт и какие-то костыли поверх IMMEDIATE/COMPILE слов.
получаем eDSL слабо интегрированный в язык (но и его может оказаться достаточно).
пятый вариант. берём язык с полноценным CTMP в духе ConvergePL. чтобы макросы компилировались в корректно типизированные AST, проверяемые во время компиляции.
и какой-то интерфейс API между compile time/runtime частями в духе $<CEI::lift(...) и т.п.
и получаем корректно типизированные макросы.
в духе первоначальной посылки pfg:
то, на чем можно создать систему необходимого качества с нуля :)
это означает, что все примитивы (концепции, парадигмы) должны быть написаны на таком системном языке, с нуля – на нём самом вся система полностью – а не только лишь отдельные части.
аки метапрог эдакий
хотя можно например использовать C библиотеки через FFI (понимая где и именно в чём эти абстракции начинают протекать и становятся unsafe).
Страуструп как-то сказал: что unsafe в С++ можно впендюрить сбоку, при большом желании.
после чего я очень засомневался в что он вообще понимает что такое unsafe. и в его проектировании, модульности (которой нет) и мультипарадигменности (плохо интегрированной между собой) и концептуальной целостности.
оставайся cfront очередным язычком уровня vala – и это можно было бы пережить.
хуже того, ведь это всё спекается в какой-то фрактал.
Ты можешь на сишных макросах сделать статические проверки ассемблерного кода, не позволяющие сунуть неправильный операнд не в ту инструкцию?
Да.
Можно даже с проверкой самих передаваемый значений, там же опкод меняется в зависимости от того, что сунули, ZP, адрес, значение или сахарок в виде A,X/A,Y/(A),Y/(A, X). Всё без проблем чекается. Мне даже в отличие от этой хаскеле-срани не нужно указывать какой тип передачи значения, я и сам могу задетектить:
// Concatenate
#define CAT__(x) x
#define CAT_(x, y) CAT__(x ## y)
#define CAT(x, y) CAT_(x, y)
// Count variadica arguments
#define ARGC(...) ARGC_(__VA_ARGS__, ARGC_RSEQ_N())
#define ARGC_(...) ARGC_N(__VA_ARGS__)
#define ARGC_N(_1, _2, N, ...) N
#define ARGC_RSEQ_N() 2, 1, 0
// Syntax routines
#define EXPAND(x) x
#define COMMA(x) ,
// OPCODES
#define OPCODE_LDA_ABSOLUTE 0xAD // LDA 0x0000..0xFFFF
#define OPCODE_LDA_ABSOLUTE_INDEXX 0xBD // LDA 0x0000..0xFFFF, X
#define OPCODE_LDA_ABSOLUTE_INDEXY 0xB9 // LDA 0x0000..0xFFFF, Y
#define OPCODE_LDA_IMMEDIATE 0xA9 // LDA #0x00..#0xFF
#define OPCODE_LDA_ZEROPAGE 0xA5 // LDA 0x00..0xFF
#define OPCODE_LDA_ZEROPAGE_INDIRECT_INDEXX 0xA1 // LDA (0x00..0xFF, X)
#define OPCODE_LDA_ZEROPAGE_INDEXX 0xB5 // LDA 0x00..0xFF, X
#define OPCODE_LDA_ZEROPAGE_INDIRECTY_INDEXY 0xB1 // LDA (0x00..0xFF), Y
#define OPCODE_CPX_ABSOLUTE 0xEC // CPX 0x0000..0xFFFF
#define OPCODE_CPX_IMMEDIATE 0xE0 // CPX #0x00..0xFF
#define OPCODE_CPX_ZEROPAGE 0xE4 // CPX 0x00..0xFF
#define OPCODE_RTS 0x60 // RTS
// VALUE CONVERTERS
// ZP tester
#define VALUE_TEST_0x00 0
#define VALUE_TEST_0x01 0
#define VALUE_TEST_0x02 0
#define VALUE_TEST_0x03 0
#define VALUE_TEST_0x04 0
#define VALUE_TEST_0x05 0
// ... I'm too lazy, works for MVP
#define VALUE_TEST_0xFF 0
// ADDRESS tester
#define VALUE_TEST_0x0001 1
#define VALUE_TEST_0x0002 1
#define VALUE_TEST_0x0003 1
#define VALUE_TEST_0x0004 1
#define VALUE_TEST_0x0005 1
// .. I'm too lazy, works for MVP
#define VALUE_TEST_0xFFFF 1
// IMMEDIATE tester
#define VALUE_TEST__0x00 2
#define VALUE_TEST__0x01 2
#define VALUE_TEST__0x02 2
#define VALUE_TEST__0x03 2
#define VALUE_TEST__0x04 2
// .. I'm too lazy, works for MVP
#define VALUE_TEST__0x05 2
// Immediate marker remover
#define VALUE_DEIM_0x00 0x00
#define VALUE_DEIM_0x01 0x01
#define VALUE_DEIM_0x02 0x02
#define VALUE_DEIM_0x03 0x03
#define VALUE_DEIM_0x04 0x04
// .. I'm too lazy, works for MVP
#define VALUE_DEIM_0xFF 0xFF
// U16 splitter
#define U16_SPLIT_0x0000 0x00, 0x00
#define U16_SPLIT_0x0001 0x01, 0x00
#define U16_SPLIT_0x0002 0x02, 0x00
#define U16_SPLIT_0x0003 0x03, 0x00
#define U16_SPLIT_0x0004 0x04, 0x00
// .. I'm too lazy, works for MVP
#define U16_SPLIT_0xFFFF 0xFF, 0xFF
// INSTRUCTION: LDA
/* Allowed LDA notations:
* a | a, x | a, y | #
* zp | (zp, x) | zp, x | (zp), y */
#define LDA_CHECKZPX(...) CAT(LDA_CHECKZPX_, ARGC(__VA_ARGS__)),
// zp
#define LDA_PROCESSOR_ARG1_1_0(val) OPCODE_LDA_ZEROPAGE, val,
// a
#define LDA_PROCESSOR_ARG1_1_1(val) OPCODE_LDA_ABSOLUTE, CAT(U16_SPLIT_, val),
// _
#define LDA_PROCESSOR_ARG1_1_2(val) OPCODE_LDA_IMMEDIATE, CAT(VALUE_DEIM, val),
// a | _ | zp
#define LDA_PROCESSOR_ARG1_1(x) CAT(LDA_PROCESSOR_ARG1_1_, CAT(VALUE_TEST_, x))(x)
// (zp, x)
#define LDA_PROCESSOR_ARG1_2_(x, reg) OPCODE_LDA_ZEROPAGE_INDIRECT_INDEXX, x,
#define LDA_PROCESSOR_ARG1_2(x) LDA_PROCESSOR_ARG1_2_ x
// a | _ | zp | (zp, x)
#define LDA_ARGC_1(x) CAT(LDA_PROCESSOR_ARG1_,ARGC(LDA_CHECKZPX x))(x)
// a,x
#define LDA_PROCESSOR_AXY_X(val) OPCODE_LDA_ABSOLUTE_INDEXX, CAT(U16_SPLIT_, val),
// a,y
#define LDA_PROCESSOR_AXY_Y(val) OPCODE_LDA_ABSOLUTE_INDEXY, CAT(U16_SPLIT_, val),
// a,x | a, y
#define LDA_PREOCESSOR_AXYZP_1(val, reg) CAT(LDA_PROCESSOR_AXY_, reg)(val)
// zp, x
#define LDA_PREOCESSOR_AXYZP_0(val, reg) OPCODE_LDA_ZEROPAGE_INDEXX, val,
// a, x | a, y | zp, x
#define LDA_PROCESSOR_ARG2_1(val, reg) CAT(LDA_PREOCESSOR_AXYZP_, CAT(VALUE_TEST_, val))(val, reg)
// (zp), y
#define LDA_PROCESSOR_ARG2_2(val, reg) OPCODE_LDA_ZEROPAGE_INDIRECTY_INDEXY, EXPAND val,
// a, x | a, y | zp, x | (zp), y
#define LDA_ARGC_2(val, reg) CAT(LDA_PROCESSOR_ARG2_, ARGC(LDA_CHECKINDIR COMMA val))(val, reg)
#define LDA(...) CAT(LDA_ARGC, CAT(_, ARGC(__VA_ARGS__)))(__VA_ARGS__)
// INSTRUCTION: RTS
// Implied only
#define RTS OPCODE_RTS,
// INSTRUCTION: CPX
// a, #, zp
#define CPX_ARGC_2(a, b) "CPX cannot be used with 2 arguments"
#define CPX_CHECKZPX(...) CAT(CPX_CHECKZPX_, ARGC(__VA_ARGS__)),
// zp
#define CPX_PROCESSOR_ARG1_1_0(val) OPCODE_CPX_ZEROPAGE, val,
// a
#define CPX_PROCESSOR_ARG1_1_1(val) OPCODE_CPX_ABSOLUTE, CAT(U16_SPLIT_, val),
// _
#define CPX_PROCESSOR_ARG1_1_2(val) OPCODE_CPX_IMMEDIATE, CAT(VALUE_DEIM, val),
// a | _ | zp
#define CPX_PROCESSOR_ARG1_1(x) CAT(CPX_PROCESSOR_ARG1_1_, CAT(VALUE_TEST_, x))(x)
// (zp, x)
#define CPX_PROCESSOR_ARG1_2_(x, reg) "CPX cannot be used with ZP-indirect syntax"
#define CPX_PROCESSOR_ARG1_2(x) CPX_PROCESSOR_ARG1_2_ x
#define CPX_ARGC_1(x) CAT(CPX_PROCESSOR_ARG1_,ARGC(CPX_CHECKZPX x))(x)
#define CPX(...) CAT(CPX_ARGC, CAT(_, ARGC(__VA_ARGS__)))(__VA_ARGS__)
const unsigned char binary[] = {
0xAD, 0x01, 0x00,
0xBD, 0x02, 0x00,
0xB9, 0x03, 0x00,
0xA9, 0x04,
0xA5, 0x05,
0xA1, 0x04,
0xB5, 0x03,
0xB1, 0x02,
0xEC, 0x01, 0x00,
"CPX cannot be used with 2 arguments"
"CPX cannot be used with 2 arguments"
0xE0, 0x04,
0xE4, 0x05,
"CPX cannot be used with ZP-indirect syntax"
"CPX cannot be used with 2 arguments"
"CPX cannot be used with 2 arguments"
0x60,
}
На компиляции, соответственно, высветит строку с ошибкой
P.S. Мне лень генерить чиселки, там сейчас саппорт от 0x0000 до 0x0005. Кому нужно - тот сам сгенерит строчки.
Лучше чем хаскель? Лучше - не требует указывать тип передачи.
Не лучше. Тип в хачкелле тоже можно не указывать, но автор библиотеки вот так вот порешил.
У хачкелловой версии есть куча плюшек. Например, вполне можно считать время выполнения кода и ставить ограничения на это. Функция больше 100500 тактов занимает? Получай ошибку! Вот ради таких плюх всё это тащемта и делают. Ну и ради лулзов, конечно же.