mirror of
https://github.com/MPSU/APS.git
synced 2025-11-20 15:00:39 +00:00
Изменение регистра в ссылках на заголовки (#151)
По умолчанию, якоря на параграфы страницы генерируются в VSCode в нижнем регистре. Гиперссылки работают нормально при просмотре страниц непосредственно в репозитории github, но при просмотре в электронной книге mdbook, эти гиперссылки не открываются. Для того чтобы они работали, необходимо чтобы регистр якорей ссылки совпадал с регистром параграфов страницы. --------- Co-authored-by: Andrei Solodovnikov <voultboy@yandex.ru>
This commit is contained in:
@@ -9,16 +9,16 @@
|
||||
## Ход работы
|
||||
|
||||
1. Изучить теорию:
|
||||
1. [Соглашение о вызовах](#соглашение-о-вызовах)
|
||||
2. [Скрипт для компоновки](#скрипт-для-компоновки-linker_scriptld)
|
||||
3. [Файл первичных команд](#файл-первичных-команд-при-загрузке-startups)
|
||||
2. [Подготовить набор инструментов для кросс-компиляции](#практика)
|
||||
1. [Соглашение о вызовах](#Соглашение-о-вызовах)
|
||||
2. [Скрипт для компоновки](#Скрипт-для-компоновки-linker_scriptld)
|
||||
3. [Файл первичных команд](#Файл-первичных-команд-при-загрузке-startups)
|
||||
2. [Подготовить набор инструментов для кросс-компиляции](#Практика)
|
||||
3. Изучить порядок компиляции и команды, её осуществляющую:
|
||||
1. [Компиляция объектных файлов](#компиляция-объектных-файлов)
|
||||
2. [Компоновка объектных файлов в исполняемый](#компоновка-объектных-файлов-в-исполняемый)
|
||||
3. [Экспорт секций для инициализации памяти](#экспорт-секций-для-инициализации-памяти)
|
||||
4. [Дизассемблирование](#дизассемблирование)
|
||||
4. [Написать и скомпилировать собственную программу](#задание)
|
||||
1. [Компиляция объектных файлов](#Компиляция-объектных-файлов)
|
||||
2. [Компоновка объектных файлов в исполняемый](#Компоновка-объектных-файлов-в-исполняемый)
|
||||
3. [Экспорт секций для инициализации памяти](#Экспорт-секций-для-инициализации-памяти)
|
||||
4. [Дизассемблирование](#Дизассемблирование)
|
||||
4. [Написать и скомпилировать собственную программу](#Задание)
|
||||
5. Проверить исполнение программы вашим процессором в ПЛИС
|
||||
|
||||
## Теория
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
> — Но разве в процессе компиляции исходного кода на языке Си мы не получаем программу, написанную на языке ассемблера? Получится ведь тот же код, что мы могли написать и сами.
|
||||
|
||||
Штука в том, что ассемблерный код, который писали ранее вы отличается от ассемблерного кода, генерируемого компилятором. Код, написанный вами, обладал, скажем так... более тонким микро-контролем хода программы. Когда вы писали программу, вы знали какой у вас размер памяти, где в памяти расположены инструкции, а где данные (ну, при написании программ вы почти не пользовались памятью данных, а когда пользовались — просто использовали случайные адреса и всё получалось). Вы пользовались всеми регистрами регистрового файла по своему усмотрению, без ограничений. Однако, представьте на секунду, что вы пишете проект на ассемблере вместе с коллегой: вы пишите одни функции, а он другие. Как в таком случае вы будете пользоваться регистрами регистрового файла? Ведь если вы будете пользоваться одними и теми же регистрами, вызов одной функции может испортить данные в другой. Поделите его напополам и будете пользоваться каждый своей половиной? Но что будет, если к проекту присоединится ещё один коллега — придётся делить регистровый файл уже на три части? Так от него уже ничего не останется. Для разрешения таких ситуаций было разработано [соглашение о вызовах](#соглашение-о-вызовах) (calling convention).
|
||||
Штука в том, что ассемблерный код, который писали ранее вы отличается от ассемблерного кода, генерируемого компилятором. Код, написанный вами, обладал, скажем так... более тонким микро-контролем хода программы. Когда вы писали программу, вы знали какой у вас размер памяти, где в памяти расположены инструкции, а где данные (ну, при написании программ вы почти не пользовались памятью данных, а когда пользовались — просто использовали случайные адреса и всё получалось). Вы пользовались всеми регистрами регистрового файла по своему усмотрению, без ограничений. Однако, представьте на секунду, что вы пишете проект на ассемблере вместе с коллегой: вы пишите одни функции, а он другие. Как в таком случае вы будете пользоваться регистрами регистрового файла? Ведь если вы будете пользоваться одними и теми же регистрами, вызов одной функции может испортить данные в другой. Поделите его напополам и будете пользоваться каждый своей половиной? Но что будет, если к проекту присоединится ещё один коллега — придётся делить регистровый файл уже на три части? Так от него уже ничего не останется. Для разрешения таких ситуаций было разработано [соглашение о вызовах](#Соглашение-о-вызовах) (calling convention).
|
||||
|
||||
Таким образом, генерируя ассемблерный код, компилятор не может так же, как это делали вы, использовать все ресурсы без каких-либо ограничений — он должен следовать ограничениям, накладываемым на него соглашением о вызовах, а также ограничениям, связанным с тем, что он ничего не знает о памяти устройства, в котором будет исполняться программа — а потому он не может работать с памятью абы как. Работая с памятью, компилятор следует некоторым правилам, благодаря которым после компиляции компоновщик сможет собрать программу под ваше устройство с помощью специального скрипта.
|
||||
|
||||
@@ -556,7 +556,7 @@ FF5FF06F 04400293 FFF00313 30529073
|
||||
|
||||
Обратите внимание что байты не просто склеились в четверки, изменился так же и порядок следования байт. Это важно, т.к. в память данные должны лечь именно в таком (обновленном) порядке байт (см. первую строчку скрипта компоновщика). Когда-то `objcopy` содержал [баг](https://sourceware.org/bugzilla/show_bug.cgi?id=25202), из-за которого порядок следования байт не менялся. В каких-то версиях тулчейна (отличных от представленного в данной лабораторной работе) вы всё ещё можете столкнуться с подобным поведением.
|
||||
|
||||
Вернемся к первой строке: `@00000000`. Как уже говорилось, число, начинающееся с символа `@` говорит САПР, что с этого момента инициализация идет начиная с ячейки памяти, номер которой совпадает с этим числом. Когда вы будете экспортировать секции данных, первой строкой будет: `@20000000`. Так произойдет, поскольку в скрипте компоновщика сказано инициализировать память данных с `0x80000000` адреса (значение которого было поделено на 4, чтобы получить номер 32-битной ячейки памяти; когда-то в objcopy был ещё один [баг](https://sourceware.org/bugzilla/show_bug.cgi?id=27214), в результате которого деление на 4 не производилось). Это было сделано, чтобы не произошло наложения адресов памяти инструкций и памяти данных (см параграф [скрипт для компоновки](#скрипт-для-компоновки-linker_scriptld)). **Чтобы система работала корректно, эту строчку необходимо удалить.**
|
||||
Вернемся к первой строке: `@00000000`. Как уже говорилось, число, начинающееся с символа `@` говорит САПР, что с этого момента инициализация идет начиная с ячейки памяти, номер которой совпадает с этим числом. Когда вы будете экспортировать секции данных, первой строкой будет: `@20000000`. Так произойдет, поскольку в скрипте компоновщика сказано инициализировать память данных с `0x80000000` адреса (значение которого было поделено на 4, чтобы получить номер 32-битной ячейки памяти; когда-то в objcopy был ещё один [баг](https://sourceware.org/bugzilla/show_bug.cgi?id=27214), в результате которого деление на 4 не производилось). Это было сделано, чтобы не произошло наложения адресов памяти инструкций и памяти данных (см параграф [скрипт для компоновки](#Скрипт-для-компоновки-linker_scriptld)). **Чтобы система работала корректно, эту строчку необходимо удалить.**
|
||||
|
||||
### Дизассемблирование
|
||||
|
||||
@@ -619,7 +619,7 @@ _Листинг 3. Пример дизасемблированного файл
|
||||
|
||||
Следующая за адресом строка, записанная в шестнадцатеричном виде — это та инструкция (или данные), которая размещена по этому адресу. С помощью этого столбца вы можете проверить, что считанная инструкция на временной диаграмме (сигнал `instr`) корректна.
|
||||
|
||||
В правом столбце находится ассемблерный (человекочитаемый) аналог инструкции из предыдущего столбца. Например, инструкция `00001197` — это операция `auipc gp,0x1`, где `gp` — это синоним (ABI name) регистра `x3` (см. параграф [Соглашение о вызовах](#соглашение-о-вызовах)).
|
||||
В правом столбце находится ассемблерный (человекочитаемый) аналог инструкции из предыдущего столбца. Например, инструкция `00001197` — это операция `auipc gp,0x1`, где `gp` — это синоним (ABI name) регистра `x3` (см. параграф [Соглашение о вызовах](#Соглашение-о-вызовах)).
|
||||
|
||||
Обратите внимание на последнюю часть листинга: дизасм секции `.data`. В этой секции адреса могут увеличиваться на любое число, шестнадцатеричные данные могут быть любого размера, а на ассемблерные инструкции в правом столбце и вовсе не надо обращать внимание.
|
||||
|
||||
@@ -645,7 +645,7 @@ _Листинг 3. Пример дизасемблированного файл
|
||||
|
||||
## Задание
|
||||
|
||||
Написать программу для вашего [индивидуального задания](../04.%20Primitive%20programmable%20device/Индивидуальное%20задание#индивидуальные-задания) к 4-ой лабораторной работе на языке C или C++ (в зависимости от выбранного языка необходимо использовать соответствующий компилятор: gcc для C, g++ для C++).
|
||||
Написать программу для вашего [индивидуального задания](../04.%20Primitive%20programmable%20device/Индивидуальное%20задание#Индивидуальные-задания) к 4-ой лабораторной работе на языке C или C++ (в зависимости от выбранного языка необходимо использовать соответствующий компилятор: gcc для C, g++ для C++).
|
||||
|
||||
Для того чтобы ваша программа собралась, необходимо описать две функции: `main` и `int_handler`. Аргументы и возвращаемые значения могут быть любыми, но использоваться они не смогут. Функция `main` будет вызвана в начале работы программы (после исполнения .boot-секции startup-файла), функция `int_handler` будет вызываться автоматически каждый раз, когда ваш контроллер устройства ввода будет генерировать запрос прерывания (если процессор закончил обрабатывать предыдущий запрос).
|
||||
|
||||
@@ -658,7 +658,7 @@ _Листинг 3. Пример дизасемблированного файл
|
||||
|
||||
Функция main может быть как пустой, содержать один лишь оператор return или бесконечный цикл — ход работы в любом случае не сломается, т.к. в стартап-файле прописан бесконечный цикл после выполнения main. Тем не менее, вы можете разместить здесь и какую-то логику, получающую данные от обработчика прерываний через глобальные переменные.
|
||||
|
||||
Доступ к регистрам контроллеров периферии осуществляется через обращение в память. В простейшем случае такой доступ осуществляется через [разыменование указателей](https://ru.wikipedia.org/wiki/Указатель_(тип_данных)#Действия_над_указателями), проинициализированных адресами регистров из [карты памяти](../13.%20Peripheral%20units#задание) 13-ой лабораторной работы.
|
||||
Доступ к регистрам контроллеров периферии осуществляется через обращение в память. В простейшем случае такой доступ осуществляется через [разыменование указателей](https://ru.wikipedia.org/wiki/Указатель_(тип_данных)#Действия_над_указателями), проинициализированных адресами регистров из [карты памяти](../13.%20Peripheral%20units#Задание) 13-ой лабораторной работы.
|
||||
|
||||
При написании программы помните, что в C++ сильно ограничена арифметика указателей, поэтому при присваивании указателю целочисленного значения адреса, необходимо использовать оператор `reinterpret_cast`.
|
||||
|
||||
@@ -755,7 +755,7 @@ void _putchar(char character);
|
||||
3. В функции `int_handler` вам необходимо считать поступившие от устройства ввода входные данные.
|
||||
4. Вам необходимо самостоятельно решить, как вы хотите построить ход работы вашей программы: будет ли ваше индивидуальное задание вычисляться всего лишь 1 раз в функции `main`, данные в которую поступят от функции `int_handler` через глобальные переменные, или же оно будет постоянно пересчитываться при каждом вызове `int_handler`.
|
||||
5. Доступ к регистрам контроля и статуса необходимо осуществлять посредством указателей на структуры, объявленные в файле [platform.h](./platform.h). В случае VGA-контроллера, доступ к областям памяти осуществляется через экземпляр структуры (а не указатель на нее), содержащий имена массивов `char_map`, `color_map` и `tiff_map`.
|
||||
5. [Скомпилируйте](#практика) программу и [стартап-файл](startup.S) в объектные файлы.
|
||||
5. [Скомпилируйте](#Практика) программу и [стартап-файл](startup.S) в объектные файлы.
|
||||
6. Скомпонуйте объектные файлы в исполняемый файл, передав компоновщику соответствующий [скрипт](linker_script.ld).
|
||||
7. Экспортируйте из объектного файла секции `.text` и `.data` в текстовые файлы `init_instr.mem`, `init_data.mem`. Если вы не создавали инициализированных статических массивов или глобальных переменных, то файл `init_data.mem` может быть оказаться пустым.
|
||||
1. Если файл `init_data.mem` не пустой, необходимо проинициализировать память в модуле `data_mem` c помощью системной функции `$readmemh` как это было сделано для памяти инструкций.
|
||||
|
||||
Reference in New Issue
Block a user