Разработка арканоида для VGA-текстового режима на Rust без использования std и alloc (создание собственной мини ОС)

Здравствуйте, уважаемые читатели!
В этой статье я хотел бы поделиться необычным и вдохновляющим проектом — реализацией арканоида в текстовом VGA-режиме, написанного полностью на Rust, без использования стандартной библиотеки и даже без аллокации памяти (#![no_std] + no_alloc).
Проект работает напрямую с VGA-памятью и PS/2 клавиатурой через порт 0x60, создавая абсолютно нативную игру в стиле 80-х, но с современным вниманием к качеству кода. И всё это — с участием милого талисмана Platinum-tan.
Зачем вообще создавать игру в текстовом режиме?
Для нас, разработчиков, работающих с высокоуровневыми фреймворками и богатыми API, подобные проекты становятся отличной возможностью:
-
Погрузиться в низкоуровневую архитектуру x86
-
Попрактиковаться в чистой архитектуре и системном программировании
-
Понять, как можно обойтись без аллокации, без стандартных зависимостей
-
И просто — почувствовать дух ретро
-
Архитектура проект.
Проект написан на Rust с использованием #![no_std] и bootimage, что позволяет компилировать игру в виде загрузочного ядра.
Основные модули:
|
Модуль |
Назначение |
|---|---|
|
|
Прямая работа с VGA-буфером |
|
|
Чтение клавиш напрямую с |
|
|
Игровая логика, отрисовка, физика мячика |
|
Точка входа, игровой цикл, обработка клавиш |
Особенности реализации
-
Мячик (
o) отскакивает от стен, блоков и платформы -
Платформа (
=======) управляется клавишамиAиD -
Цвета блоков зависят от строки (радуга в ASCII)
-
Поддержка
SPACEдля начала иRдля рестарта -
Вывод очков и милых сообщений от Platinum-tan
Немного магии: как работают цвета.
ColorCode::new(Color::Red, Color::Black)
Каждый символ в VGA-буфере имеет не только ASCII-байт, но и байт цвета. Мы изменяем эти цвета на лету — и получаем полноцветный «текстовый» арканоид..
Чтение клавиш без драйвера.
pub unsafe fn inb(port: u16) -> u8 {
let value: u8;
core::arch::asm!(\"in al, dx\", out(\"al\") value, in(\"dx\") port);
value
}
Клавиши считываются напрямую из порта 0x60 — без драйверов, без ОС, просто железо и вы.
Вывод информации в виртуальную машину кастомный и реализован через vga_buffer так как мы не можем использовать вывод с использованием no_std
...
impl Writer {
pub fn new() -> Self {
Self {
column_position: 0,
color_code: ColorCode::new(Color::Yellow, Color::Black),
buffer: unsafe { &mut *(VGA_BUFFER_ADDR as *mut Buffer) },
}
}
pub fn write_byte(&mut self, byte: u8) {
match byte {
b'\n' => self.new_line(),
byte => {
if self.column_position >= BUFFER_WIDTH {
self.new_line();
}
let row = BUFFER_HEIGHT - 1;
let col = self.column_position;
self.buffer.chars[row][col].write(ScreenChar {
ascii_character: byte,
color_code: self.color_code,
});
self.column_position += 1;
}
}
}
pub fn write_string(&mut self, s: &str) {
for byte in s.bytes() {
match byte {
0x20..=0x7e | b'\n' => self.write_byte(byte),
_ => self.write_byte(0xfe),
}
}
}
pub fn new_line(&mut self) {
for row in 1..BUFFER_HEIGHT {
for col in 0..BUFFER_WIDTH {
let c = self.buffer.chars[row][col].read();
self.buffer.chars[row - 1][col].write(c);
}
}
self.clear_row(BUFFER_HEIGHT - 1);
self.column_position = 0;
}
fn clear_row(&mut self, row: usize) {
let blank = ScreenChar {
ascii_character: b' ',
color_code: self.color_code,
};
for col in 0..BUFFER_WIDTH {
self.buffer.chars[row][col].write(blank);
}
}
/// Выводит строку по центру последней строки экрана
pub fn write_centered(&mut self, text: &str) {
let len = text.len().min(BUFFER_WIDTH);
let padding = (BUFFER_WIDTH - len) / 2;
self.column_position = padding;
self.write_string(text);
}
...
Как собрать и запустить
Установите Rust nightly и bootimage:.
rustup install nightly
rustup component add rust-src --toolchain nightly
cargo install bootimage
Соберите ядро:.
cargo +nightly bootimage -Z build-std=core,compiler_builtins --target x86_64-platinum_os.json -Z build-std-features=compiler-builtins-mem --target-dir target/x86_64-platinum_os
Запустите в QEMU:.
qemu-system-x86_64 -drive format=raw,file=target\x86_64-platinum_os\x86_64-platinum_os\debug\bootimage-platinum_os.bin -serial stdio -no-reboot -no-shutdown
Демо: Скриншот.

Вдохновение
Это не просто технический эксперимент — это способ выразить любовь к железу, к языку Rust, и к эстетике ASCII-графики. Если вы когда-нибудь мечтали создать свою игру без ОС — сейчас самое время..
Репозиторий и весь код: https://github.com/digkill/platinum-os-arkanoid
Спасибо за внимание!.
Лидер канадской НДП Ави Льюис раскритиковал закрытие BioWare и предложил помочь сотрудникам выкупить студию у EA
Глава Saber Interactive считает AA-игры вроде Hellraiser: Revival будущим индустрии
Два дополнения для The Elder Scrolls Online раздают бесплатно поклонникам Skyrim
Проблемы Xbox сказались на зарплате главы Microsoft
Энтузиаст перенёс GTA IV на PS5, часть работы выполнила нейросеть
Resonance: A Plague Tale Legacy оказалась ниже ожиданий издателя
8BitDo начала принимать предзаказы на контроллер для файтингов Arcade Controller Pro
Игроки считают, что Mass Effect 5 ещё можно спасти, и запустили петицию