Ответ на пост «На чём писали предки»6
Отважился, расколупал. И теперь вот думаю: то ли почистить этот артефакт, и оставить, как есть, то ли сотворить новую клаву на бесконтакных ПКБ9-2?
UPD: Всё-таки будет новая клава на ПКБ. Заодно и мозг разомну, давненько я таких задачек не решал.
Продолжение поста «На чём писали предки»6
Ещё через пару лет я поступил в технарь, точнее тогда ужЕ колледж, и там увидел настоящего монстра! Знакомьтесь: ИЗОТ 1016С. Этот аппарат жил в двух комнатах - в одной он сам, а в другой его диски. По всему технарю были раскиданы терминалы, иногда в самых неожиданных местах. Зелёные монохромные мониторы, цветом точно как в Матрице и удобные неубиваемые клавы. Опять Бейсик - только теперь есть полноценная ОС с дисками, разделами, файлами. Очень интересно было в этом всём ковыряцца! Текстовый редактор правда был, жутко упоротый. если вдруг надо отредактировать - то прям ваще беда! Типа:
EDIT <текст - что меняем>@<текст - на что меняем>@<число - сколько раз в тексте встречается то что меняем>
Зато преподша по информатике была просто агонь - лет 20 наверное. местами было очень сложно сосредоточиться - особенно летом ;)
Поучаствовал в олимпиаде по программированию. Слил бездарно! Взял сразу самую сложную задачу, начал делать, не продумав до конца. А когда понял, что зашёл в тупик и надо переделывать время уже заканчивалось. Правда потом через пару недель слышу одногруппники суетятся: "там Бороде нашему премию дали". (несмотря на 15 лет - Борода это мне кликуху навесили ещё в первый день подкурсов - догадайтесь почему) Короче дали денях три с половиной тыщи (стипендия была 12). я обрадовался конечно, но помню неудобно было и казалось слишком много за просто участие.
ЗЫ. На ИЗОТе была игра змейка. по легенде её много лет назад кто-то то ли написал, то ли принёс откуда-то. с ней весь педагогический состав боролся нещадно. удаляли её по всем разделам, по всем дискам, углам и закоулкам, искали, чистили. БЕСПОЛЕЗНО! Змейка всё равно выживала и вскоре снова расползалась, занимая бесценное дисковое пространство своими неисчислимыми копиями)))
ЗЗЫ. а в учительской стоял 286-й. на нём был принц персии. сука каким же он казался красивым и не побоюсь этого слова - реалистичным!
Продолжение поста «На чём писали предки»6
Через пару лет после БК у меня появился вот этот широко известный персонаж. У нас его Синклер называли. На нём правда большинство кроме "лоад две кавычки энтер" ничего не писали))) Тем не менее Бейсик в нём был прикольный. Название оператора не надо было набирать - оно сразу по нажатию кнопки выскакивало. И синтаксис он проверял не при запуске программы, а при вводе каждой строки. И если чо не так - вопросиком подсвечивал место и строку ввести не давал. Умный сцуко шопесец! Цветов в четыре раза больше - целых 16, так что рисовать на нём уже поинтересней было. Ну и любимая шутеечка:
10 GOSUB 10
RUN
ЗЫ. Элита конечно поражала воображение своим размахом, объёмом и количествами. с трудом верилось как в такой маленькой штуке такой огромный мир помещаецца.
ЗЗЫ. день его приобретения был очень насыщенным. ранним утром на радиорынке удалось наконец продать БК. И сразу купить его. Приехали с друганом его подключать, уже к цветному рекорду - и спалили телик. Сразу нашли объяву в Ва-Банке и вызвали мастера. Он быстро приехал и всё починил. Правда удивился, как мы умудрились ушатать и яркостной и питание к яркостному. Но главное, что мама об этом ничо не узнала)))
Ответ на пост «На чём писали предки»6
через пару лет после мк-52 у меня появился вот этот зверюга - БК 0010-01. в нём был вшитый бейсик и на "флэшке" был язык "Фокал". подключал я его к ч/б телеку "Рекорд". игры загружал с аудиокассет, причём у меня был такой магнитофон, у которого мощность выхода зависела от громкости. Поэтому приходилось выкручивать громкость на максимум и закрывать мафон подушкой))). после уроков ко мне полкласса ходило играть. самая крутяцкая была игра "Десантник" из четырёх частей. ну и много было клонов спектрумовских игрушек.
бейсик и фокал освоил полностью, особенно рисовать любил в нём всякое. Но вот там была возможность делать в бейсике вставки на ассемблере. это у меня так и не получилось((( остался можно сказать незакрытый гештальт...
Ответ на пост «На чём писали предки»6
Я вот на этом в 10 лет начал. Весч! 105 шагов программы, 15 регистров памяти и ещё четыре регистра стек. Игры были на нём даже реалтаймовые - учебный полёт, гонки на санях. И пошаговые - охота на LIS. Блок расширения памяти был - флэшка по-нашему. Кто на нём считать пытался всегда удивлённо спрашивал "а где равно?"
Путь от Кода до Бинарного Файла
Как же исходный код превращается в бинарный файл, который потом исполняется на компьютере? Не нашёл ни одной статьи, которая описывала бы полный процесс от начала до конца, поэтому я написал данный материал.
Как оказалось, не всё так трудно, как мне изначально казалось. Я понимаю, что в настоящих больших компиляторах всё гораздо сложнее, но это не меняет принципа по которым они строятся и работают.
Этапы
1. Lexing
На этом этапе исходный код в виде строки разделяется на отдельные части, то есть токены. Этот этап – самый простой во всём процессе компиляции.
Вход
let a = 10 + 2
if a > 8 then
debug "A больше 8"
else
debug "А либо меньше, либо равно 8"
end
Выход
[
Let, Identifier("a"), Equal, Integer(10), Plus, Integer(2),
If, Identifier("a"), Greater, Integer(8), Then,
Debug, String("A больше 8"),
Else,
Debug, String("А либо меньше, либо равно 8"),
End,
]
2. Parsing
Здесь поток токенов объединяется в AST или абстрактное синтаксическое дерево. В этом дереве содержится вся информация об исходном коде в структурированном виде, удобным для обработки и анализа. Например, с его помощью можно проверять корректность типов переменных.
[
Let {
identifier: "a",
value: Binary(Add, Integer(10), Integer(2)),
},
If {
condition: Binary(Greater, Identifier("a"), Integer(8)),
then: [Debug(String("A больше 8"))],
else_: [Debug(String("А либо меньше, либо равно 8"))],
},
]
3. Промежуточное представление (IR)
AST преобразуется в низкоуровневые инструкции, которые не зависят от конкретной архитектуры. Это удобно, так как упрощает поддержку большого количества архитектур и процессоров.
В компиляторах Rust и Clang в качестве промежуточного представления используется LLVM IR, так как его экосистема берёт на себя многие оптимизации, и компилирование в ассемблерный код для разных платформ как X86, ARM и так далее.
Граф потока управления (CFG)
Сначала, для того, чтобы избавится от условных конструкций как if и match, мы разделяем входное дерево на отдельные блоки, которые не содержат в себе условий, и дальше связываем их, описывая переходы между ними.
Блоки, не содержащие условий
{
0: [
Let {
identifier: "a",
value: Binary(Add, Integer(10), Integer(2)),
},
],
1: [Debug(String("A больше 8"))],
2: [Debug(String("А либо меньше, либо равно 8"))],
3: Empty,
}
Блок Empty – это пустой блок, который не содержит в себе инструкций, и служит только для удобства построения CFG.
И условные переходы между блоками
{
0: Branch { # переход с условием
condition: Binary(Greater, Identifier("a"), Integer(8)),
true_: 1,
false_: 2,
},
1: Direct(3), # прямой переход без условия
2: Direct(3),
}
Трёхадресный код (3AC)
Состоит из низкоуровневых инструкций максимально приближенных к нативному ассембли-коду.
Первый блок
[
Label(0),
LoadInteger { to: 0, value: 10 },
LoadInteger { to: 1, value: 2 },
Add { to: 2, left: 0, right: 1 },
Set { identifier: "a", from: 2 },
Get { to: 3, from: "a" },
LoadInteger { to: 4, value: 8 },
Greater { to: 5, left: 3, right: 4 },
JumpIf { condition: 5, label: 1 },
Jump(2),
Второй
Label(1),
LoadString { to: 6, value: "A больше 8" },
Debug { value: 6 },
Get { to: 7, from: "a" },
LoadInteger { to: 8, value: 8 },
Greater { to: 9, left: 7, right: 8 },
JumpIf { condition: 9, label: 3 },
Jump(2),
Третий
Label(2),
LoadString { to: 10, value: "А либо меньше, либо равно 8" },
Debug { value: 10 },
И последний, пустой блок
Label(3),
]
Или в виде псевдо-кода
@0:
#0 = 10
#1 = 2
#2 = add #0 #1
$a = #2
#3 = $a
#4 = 8
#5 = gt #3 #4
jump @1 if #5
jump @2
@1:
#6 = "A больше 8"
debug #6
#7 = $a
#8 = 8
#9 = gt #7 #8
jump @3 if #9
jump @2
@2:
#10 = "А либо меньше, либо равно 8"
debug #10
@3:
4. Ассембли
Далее, каждая 3AC инструкция конвертируется в одну или несколько ассемблерных инструкций, которые уже напрямую выполняются на процессоре без какой-либо прослойки.
section .data
str_0: db "A больше 8", 0
str_1: db "А либо меньше, либо равно 8", 0
Строки будут записаны вместе с файлом как его часть, то есть они не будут аллоцированны динамически во время выполнения.
section .bss
a: resq 1
Мы будем хранить переменную в секции .bss, так как в нашей программе одна зона видимости. В настоящих компиляторах переменные обычно хранятся на стеке, или вовсе в регистрах в зависимости от степени оптимизации.
section .text
global _start
_start:
Делаем _start глобально видимым для того, чтобы линкер смог собрать бинарный файл.
L0:
mov rax, 10
mov rbx, 2
mov rcx, rax
add rcx, rbx
mov [a], rcx
a = rcx = rax + rbx = 10 + 2 = 12.
mov rax, [a]
mov rbx, 8
cmp rax, rbx
mov rcx, 0
setg cl
rcx = rax > rbx = a > 8 = 1 то есть true.
cmp rcx, 1
je L1
jmp L2
Если rcx = 1, то есть true, то переходим в L1, иначе – в L2.
L1:
mov rax, str_0
call debug
debug – это какая-то функция, которая печатает строки в консоль. В целях соблюдения компактности, я не стал её включать в код. Регистр rax – первый аргумент.
mov rax, [a]
mov rbx, 8
cmp rax, rbx
setg rcx
cmp rcx, 1
je L3
jmp L2
L2:
mov rax, str_1
call debug
L2 – начало блока else.
L3:
mov rax, 60
mov rdi, 0
syscall
Выходим из программы, производя системный вызов (syscall). В rax находится номер вызова – 60, то есть выход (SYS_exit). А в rdi лежит статус завершения программы, в данном случае 0, то есть успешное завершение.
Полезное
Заключение
Надеюсь вам понравилась эта статья! Она написана на основе моего хобби-компилятора, поэтому если у вас есть желание внести свою лепту в проект – отправляйте пул-реквест в репозиторий!







