Использую нейронку как компилятор

от автора

Введение

Бла-бла-блаааа бла-бла-блаааа-беееее-бууууу бла-бла-бла бле-бле-бле

Основная часть

ты - компилятор Си с gnu расширениями,который был создан специально для написания программ для x16-PRos.стандартная библиотека уже включена в компилятори может быть выключена на уровне флагов (-fnostdlib).так же компилятор потдерживает флаг -outformat=ple/rawпри raw - выходит плаский бинарник для загрузки с org 0x8000,а при ple - полноценный ple файл

С такого простенького неумелого промта для gemeni мы начнем наш прикол.

После небольших вопросов, уточнений и тд мы можем дать ей простенькую программу

Проверка знаний о x16-PRos
раскажи о формате pleФормат .PLE (PRos Large Executable)— это специализированный нативный формат исполняемых файловдля операционной системы x16-PRos.В отличие от простых плоских бинарников (.BIN / .COM),которые просто загружаются в памятьи выполняются с фиксированного адреса (например, org 0x8000),формат .PLE спроектирован для поддержки расширенных возможностейсовременной ОС в рамках 16-битного реального режима.Исполняемые файлы .PLE обладают тремя ключевымиархитектурными особенностями:1. Поддержка многозадачности (Multitasking)  Формат .PLE интегрирован с подсистемой кооперативной многозадачности  ядра x16-PRos.Такие программы могут выполняться параллельно  с другими процессами.  Они поддерживают фоновый режим работы:  в терминале ОС реализована специальная команда  bg <file>.ple, позволяющая запустить программу  "в бэкграунде".2. Интеграция с графическим интерфейсом (GUI)  В то время как стандартные консольные утилиты работаютвнутри текстового  терминала PRos Terminal, файлы формата .PLE имеют встроенную поддержку  графической оболочки.  Каждое GUI-приложение в формате .PLE запускается в собственном кастомизируемом  окне. Формат разработан с учетом взаимодействия с системным оконным менеджером,  обработкой событий окон и управления через драйвер мыши (PS/2 / USB).3. Структурированный заголовок (Header)  Обычный бинарный файл не содержит метаданных.  Файл .PLE содержит заголовок, который сообщает загрузчику ядра необходимую  техническую информацию:    Сигнатуру формата для валидации файла перед запуском.    Требуемый объем памяти под стек и динамические данные.    Точку входа (Entry Point), отличную от стандартного смещения 0x0000 внутри сегмента.  Расположение в системе Ядро x16-PRos имеет жесткое разграничение по путям  поиска исполняемых файлов:  файлы .BIN автоматически ищутся в системной директории BIN/,  в то время как для приложений нового формата выделена собственная директория  PLE/.  ...

это была не первая итерация и ссылка на проект явно помогла ей разобраться

Наш первый тест:

test.c:#include <stdio.h>void main(){    puts("Hello, from AIC");}aic -o test.bin -outformat=raw test.c

Да я назвал компилятор AIC что буквально Artificial Intelligence C — ИИ-шный Си

Но даже для такого простого примера и имея ссылку на проект нейронка захотела использовать стандартные прерывания биоса, а не готовые PRos-овские.

; Generated by AIC compiler for x16-PRos; Command: aic -o test.bin -outformat=raw test.c[BITS 16][ORG 0x8000]            ; Смещение raw-загрузки в x16-PRosstart:    call main           ; Вызов главной функции приложения        ; Завершение работы программы и возврат в PRos Terminal    mov ah, 0x4C        ; Стандартный выход (совместимость с MS-DOS/PRos)    int 0x21    retmain:    push bp    mov bp, sp    ; Эквивалент puts("Hello, from AIC");    mov si, msg_hello    call print_string    mov sp, bp    pop bp    ret; --- Встроенная подфункция libc для вывода строки ---print_string:    pusha    mov ah, 0x0E        ; BIOS Телетайп-вывод символа    mov bl, 0x0F        ; Цвет шрифта (белый на чёрном).loop:    lodsb               ; Загрузить следующий символ из DS:SI в AL    cmp al, 0    je .done            ; Если дошли до нуля (null-terminator), выходим    int 0x10            ; Видео-прерывание BIOS    jmp .loop.done:    ; Вывод перевода строки \n (CR + LF)    mov al, 0x0D    int 0x10    mov al, 0x0A    int 0x10    popa    retsection .data    msg_hello db "Hello, from AIC", 0

Забавно ещё что он пытается использовать DOS-овское прерывание 0x21 и его функцию 0x4C, хотя в PRos насколько я помню она не реализована (но тут могу ошибаться (PRox, поправит меня в коментах))

Вносим корректировки и даем ссылку на API проса:

puts использует внутрение прерывания PRos как и вся stdlib.давай минимум коментариев, просто код на nasm.формат выходных файлов теперь всегда .asm а -outformat по умолчанию raw.сгенерированые asm файлы не должны иметь коментариев кроме технических для отладки и тп.https://github.com/PRoX2011/x16-PRos/blob/dev/docs/API.md

Долгожданный код от нейронки:

[BITS 16][ORG 0x8000]start:    call main    mov ah, 0x4C    int 0x21    retmain:    push bp    mov bp, sp    mov si, msg_hello    call puts    mov sp, bp    pop bp    retputs:    push ax    mov ah, 0x01    int 0x21    pop ax    retsection .data    msg_hello db "Hello, from AIC", 0

Теперь гемени видимо плохо читал спецификацию cstdlib на выходных и позабыл что puts должен сам переводить строку. Хотя в предыдущем коде он это предусматривал.
В любом случае самое время увидеть наше творение в действии.

Чтож, оно работает. уже хорошо (несмотря на выход через прерывание).

printf

Да, пожалуй довайте дадим гемени попотеть и заставим написать на real-mode ассемблере парсинг printf.

```#include <stdio.h>void main(){    int a = 54;    printf("num is: %d\n", a);}```aic -o test.asm -O0 test.c

добавил -O0 чтобы не хитрила с инлайнингом.

[BITS 16][ORG 0x8000]start:    call main    mov ah, 0x4C    int 0x21    retmain:    push bp    mov bp, sp    sub sp, 2    mov word [bp-2], 54    push word [bp-2]    mov ax, fmt_num    push ax    call printf    add sp, 4    mov sp, bp    pop bp    retprintf:    push bp    mov bp, sp    pusha    mov si, [bp+4]    lea bx, [bp+6].loop:    lodsb    cmp al, 0    je .done    cmp al, '%'    je .format    call print_char    jmp .loop.format:    lodsb    cmp al, 'd'    je .print_int    cmp al, '%'    je .percent    jmp .loop.percent:    mov al, '%'    call print_char    jmp .loop.print_int:    mov ax, [bx]    add bx, 2    call print_number    jmp .loop.done:    popa    mov sp, bp    pop bp    retprint_char:    push ax    push si    mov byte [char_buf], al    mov byte [char_buf+1], 0    mov si, char_buf    mov ah, 0x01    int 0x21    pop si    pop ax    retprint_number:    pusha    mov cx, 0    mov bx, 10.convert:    mov dx, 0    div bx    push dx    inc cx    cmp ax, 0    jne .convert.print_digits:    pop ax    add al, '0'    call print_char    loop .print_digits    popa    retsection .data    fmt_num db "num is: %d", 0x0D, 0x0A, 0    char_buf db 0, 0

на мое удивление это достаточно хорошо на первый взгляд чтобы работать. Она не стала писать весь форматер, да и я сомневаюсь что это соответствует cdecl, но все же.

Знаете, у меня даже уважение появилось к гемени и инженерам гугла в целом.

Это вам не «напиши мне приложение чтобы оно…» тут надо быть… ну как минимум не трезвым чтобы таким заниматься…

Простенький парсер конфигов

Просто на ходу мной был придуман формат конфигов типа такого

.my_var1=hello, world;.my_var2=43;.548=my_val;

ну и парсер для него был накидан за… да за минуту

#include <stdio.h>#include <pros/api.h>char file_text[1024];void main(){    /* читает текст файла в буфер как null-terminated строку */    read_file_text("testc.ccc", &file_text, 1024 /* размер буфера */);    int pos = 0;    char c = file_text[pos];    while (c) {        if (c == '.') {            char buf[32];            int i = 0;            while (c != '=') {                buf[i] = c;                c = file_text[pos++];                i++;            }            buf[i] = 0;            pos++;            c = file_text[pos++];            char val[32];            while (c != ';') {                val[i] = c;                c = file_text[pos++];                i++;            }            val[i] = 0;            printf("%s: %s\n", buf, val);        }        c = file_text[pos++];    }}

С сахором в виде функции read_file_text якобы из pros/api.h.
Важно сказать что смотреть на безопасность и красоту этого кода не нужно — мы вообщето нейронкой Си компилируем.

ну и скормив этот код нашему «компилятору» получаем такое нечто:

[BITS 16][ORG 0x8000]start:    call main    mov ah, 0x4C    int 0x21    retmain:    push bp    mov bp, sp    sub sp, 70    push 1024    mov ax, file_text    push ax    mov ax, str_filename    push ax    call read_file_text    add sp, 6    mov word [bp-2], 0    mov bx, [bp-2]    mov al, [file_text + bx]    mov [bp-3], al.while_c:    mov al, [bp-3]    cmp al, 0    je .end_while_c    cmp al, '.'    jne .end_if_dot    mov word [bp-37], 0.while_not_equal:    mov al, [bp-3]    cmp al, '='    je .end_while_not_equal    mov bx, [bp-37]    lea dx, [bp-35]    add bx, dx    mov [bx], al    mov bx, [bp-2]    inc word [bp-2]    mov al, [file_text + bx]    mov [bp-3], al    inc word [bp-37]    jmp .while_not_equal.end_while_not_equal:    mov bx, [bp-37]    lea dx, [bp-35]    add bx, dx    mov byte [bx], 0    inc word [bp-2]    mov bx, [bp-2]    inc word [bp-2]    mov al, [file_text + bx]    mov [bp-3], al.while_not_semicolon:    mov al, [bp-3]    cmp al, ';'    je .end_while_not_semicolon    mov bx, [bp-37]    lea dx, [bp-69]    add bx, dx    mov [bx], al    mov bx, [bp-2]    inc word [bp-2]    mov al, [file_text + bx]    mov [bp-3], al    inc word [bp-37]    jmp .while_not_semicolon.end_while_not_semicolon:    mov bx, [bp-37]    lea dx, [bp-69]    add bx, dx    mov byte [bx], 0    lea ax, [bp-69]    push ax    lea ax, [bp-35]    push ax    mov ax, fmt_str    push ax    call printf    add sp, 6.end_if_dot:    mov bx, [bp-2]    inc word [bp-2]    mov al, [file_text + bx]    mov [bp-3], al    jmp .while_c.end_while_c:    mov sp, bp    pop bp    retread_file_text:    push bp    mov bp, sp    mov ax, 0x02    int 0x21    pop bp    retprintf:    push bp    mov bp, sp    pusha    mov si, [bp+4]    lea bx, [bp+6].loop:    lodsb    cmp al, 0    je .done    cmp al, '%'    je .format    call print_char    jmp .loop.format:    lodsb    cmp al, 's'    je .print_str    cmp al, '%'    je .percent    jmp .loop.percent:    mov al, '%'    call print_char    jmp .loop.print_str:    mov dx, si    mov si, [bx]    add bx, 2.str_loop:    lodsb    cmp al, 0    je .str_done    call print_char    jmp .str_loop.str_done:    mov si, dx    jmp .loop.done:    popa    mov sp, bp    pop bp    retprint_char:    push ax    push si    mov byte [char_buf], al    mov byte [char_buf+1], 0    mov si, char_buf    mov ah, 0x01    int 0x21    pop si    pop ax    retsection .data    str_filename db "testc.ccc", 0    fmt_str db "%s: %s", 0x0D, 0x0A, 0    char_buf db 0, 0section .bss    file_text resb 1024

Вот его я уже не хотел проверять вообще и надеялся что нейронка ничего не заменила и не добавила больше правил чем нужно, ведь без них веселее.

Если все же посмотреть в функцию read_file_text то можно увидеть что нейронка все ещё не знает как именно работают прерывания в PRos.

После пары итераций ошибок и бросания кусками текста прямо из API.md я смог добиться хоть сколько-нибудь удовлетворяющего, на первый взгляд, меня варианта:

[BITS 16][ORG 0x8000]start:    call main    mov ah, 0x4C    int 0x21    retmain:    push bp    mov bp, sp    sub sp, 72    push 1024    mov ax, file_text    push ax    mov ax, str_filename    push ax    call read_file_text    add sp, 6    mov [bp-2], ax    cmp word [bp-2], 0    je .err_ok    mov si, msg_err    call puts    mov sp, bp    pop bp    ret.err_ok:    mov word [bp-4], 0    mov bx, [bp-4]    mov al, [file_text + bx]    mov [bp-5], al.while_c:    mov al, [bp-5]    cmp al, 0    je .end_while_c    cmp al, '.'    jne .end_if_dot    mov word [bp-39], 0.while_not_equal:    mov al, [bp-5]    cmp al, '='    je .end_while_not_equal    mov bx, [bp-39]    lea dx, [bp-37]    add bx, dx    mov [bx], al    mov bx, [bp-4]    inc word [bp-4]    mov al, [file_text + bx]    mov [bp-5], al    inc word [bp-39]    jmp .while_not_equal.end_while_not_equal:    mov bx, [bp-39]    lea dx, [bp-37]    add bx, dx    mov byte [bx], 0    inc word [bp-4]    mov bx, [bp-4]    inc word [bp-4]    mov al, [file_text + bx]    mov [bp-5], al.while_not_semicolon:    mov al, [bp-5]    cmp al, ';'    je .end_while_not_semicolon    mov bx, [bp-39]    lea dx, [bp-71]    add bx, dx    mov [bx], al    mov bx, [bp-4]    inc word [bp-4]    mov al, [file_text + bx]    mov [bp-5], al    inc word [bp-39]    jmp .while_not_semicolon.end_while_not_semicolon:    mov bx, [bp-39]    lea dx, [bp-71]    add bx, dx    mov byte [bx], 0    lea ax, [bp-71]    push ax    lea ax, [bp-37]    push ax    mov ax, fmt_str    push ax    call printf    add sp, 6.end_if_dot:    mov bx, [bp-4]    inc word [bp-4]    mov al, [file_text + bx]    mov [bp-5], al    jmp .while_c.end_while_c:    mov sp, bp    pop bp    retread_file_text:    push bp    mov bp, sp    push bx    push cx    push si    mov si, [bp+4]    mov cx, [bp+6]    mov ah, 0x02    int 0x22    jc .error        mov si, cx    add si, bx    mov byte [si], 0    mov ax, 0    jmp .done.error:    mov ax, 1.done:    pop si    pop cx    pop bx    pop bp    retputs:    push ax    mov ah, 0x01    int 0x21    pop ax    retprintf:    push bp    mov bp, sp    pusha    mov si, [bp+4]    lea bx, [bp+6].loop:    lodsb    cmp al, 0    je .done    cmp al, '%'    je .format    call print_char    jmp .loop.format:    lodsb    cmp al, 's'    je .print_str    cmp al, '%'    je .percent    jmp .loop.percent:    mov al, '%'    call print_char    jmp .loop.print_str:    mov dx, si    mov si, [bx]    add bx, 2.str_loop:    lodsb    cmp al, 0    je .str_done    call print_char    jmp .str_loop.str_done:    mov si, dx    jmp .loop.done:    popa    mov sp, bp    pop bp    retprint_char:    push ax    push si    mov byte [char_buf], al    mov byte [char_buf+1], 0    mov si, char_buf    mov ah, 0x01    int 0x21    pop si    pop ax    retsection .data    str_filename db "testc.ccc", 0    msg_err db "cant read a file", 0x0D, 0x0A, 0    fmt_str db "%s: %s", 0x0D, 0x0A, 0    char_buf db 0, 0section .bss    file_text resb 1024

и… он все ещё не читает файл…

Я помучал нейронку ещё, и даже начал просто ей писать «файл не читаеться», но ни к чему это не привело.
Может ошибка в моем коде, может в PRos, может нейронка намудрила.

В любом случае я особо и не расчитывал на такие простые успехи, но заниматься этим полноценно у меня желания нет…

Полный конвеер компиляции через нейронку

Ради прикола довайте заставим уже в другом диалоге того-же гемени переводить наш ассемблер в base64 чтобы потом записать это в файл и запустить программу полностью собраную нейронкой.

[BITS 16][ORG 0x8000]start:    call main    mov ah, 0x4C    int 0x21    retmain:    push bp    mov bp, sp    mov si, msg_hello    call puts    mov sp, bp    pop bp    retputs:    push ax    mov ah, 0x01    int 0x21    pop ax    retsection .data    msg_hello db "Hello, from AIC", 0переведи этот ассемблер в готовый сырой поток байт как при nasm -fbin и выдай base64 код
Вот готовый Base64-код скомпилированного бинарного потока (как при использовании команды nasm -fbin):6AUAtEzNIcNVieW+HIDoBACJ7F3DULQBzSFYw0hlbGxvLCBmcm9tIEFJQwA=## Разбор структуры по байтам (Hex-дамп)Для наглядности ниже представлен шестнадцатеричный дамп (44 байта) с разбором инструкций:E8 05 00        ; call main (относительное смещение +5 байт)B4 4C           ; mov ah, 0x4CCD 21           ; int 0x21C3              ; ret55              ; push bp89 E5           ; mov bp, spBE 1C 80        ; mov si, 0x801C (адрес msg_hello с учетом ORG 0x8000)E8 04 00        ; call puts (относительное смещение +4 байта)89 EC           ; mov sp, bp5D              ; pop bpC3              ; ret50              ; push axB4 01           ; mov ah, 0x01CD 21           ; int 0x2158              ; pop axC3              ; ret; Строка "Hello, from AIC\x00" (смещение 0x801C)48 65 6C 6C 6F 2C 20 66 72 6F 6D 20 41 49 43 00------------------------------💡 Если вам понадобятся дополнительные изменения в коде (например, исправление функции puts, так как в данном коде прерывание int 0x21 с ah=0x01 ожидает ввод символа с клавиатуры вместо вывода строки si), дайте знать, и я помогу переписать ассемблерный код или пересобрать бинарник!

Пойдеееет.

Пишем в файл

[System.IO.File]::WriteAllBytes("./ai.bin", [System.Convert]::FromBase64String("6AUAtEzNIcNVieW+HIDoBACJ7F3DULQBzSFYw0hlbGxvLCBmcm9tIEFJQwA="))  

Да я свиндусятник, не удивляйтесь. Пишите кстати если хотите статью как мы пишем ченить на powershell (там есть на что помотреть, я вас уверяю)

mcopy -i disk_img/x16pros.img ./ai.bin ::/BIN.DIR/

Да, я свиндусятник с wsl, не удивляйтесь.

Нууу, я не удивлен… не сильно то сложный код был.


Итог

Ээээмммм, да мне просто нечего было делать, я давно ещё этот прикол обнаружил и сейчас решил написать не то что-бы статью, а скорее поток действий, тк писал я стать. Паралельно с тем как вообще занимался всей этой фигней, и в общем все это заняло мение часа-полутора.

Я думаю если взять нейронку поумнее, промты по лучше, код по безопаснее, то что-то да из этого выйдет. Каков вообще шанс что вайбкодинг перерастет во что-то вот такое и в сурсах вайб-проектов будут просто истории терминалов с диалогами вот таких вот «компиляторов».

А нормальные полноценные статьи от меня будут совсем скоро… покрайней мере я на это надеюсь:
SectorOS ещё обзаведется директориями и новыми программами (может даже совместимостью с x16-PRos).
SFS32 — файловая система на базе SFS тоже уже есть и полноценная 32х битная ось тоже не за горами.
Главный мой проект, который я не показываю тоже уже почти готов, но статья о нем скорее всего будет зимой когда он будет уже в состоянии конкурировать с другими решениями.
Большой цикл статей с очень интересной темой есть у меня в голове, но думаю писать я его начну в… как только так сразу.

У меня была идея сделать ТГК и публиковать туда не только развлекательный контент, но и позновательный и более узконаправленый вглубь наших компудахтеров, держать таким образом аудиторию, но думаю пока придержать эту идею. (я вообще хз есть ли тут те, кто прям знает меня как автора целых ДВУХ СТАТЕЙ, это вам не это)

диалог с «компилятором»: https://share.google/aimode/MKCLPmxxM48WjFHgr
не могу не оставить ссылку на сам x16-PRos: https://github.com/PRoX2011/x16-PRos

Всем спасибо за прочтение. Всех с протеряным летом.

ссылка на оригинал статьи https://habr.com/ru/articles/1068062/