Потому что bochs.exe без dbg не понимает опцию gui_debug и magic_break. Для них надо два разных bxrc файлы заводить.
KIV Всё заработало. А вот через что удобно вообще осуществлять сборку Floppy-образа? А то ведь понимаю я что мой алгоритм: получение из ASM байтов, открытие HEX-редактора для вставки полученных байтов в начало файла на 1.44 МБ...издевательский.
Code (Text): org 0x7C00 ... rb 510 - ($ - $$) db 0x55,0xAA rb (2880 * 512) - ($ - $$) - 1 db 0 Это самый простой вариант - получается файл с 512 байтами загрузчика и 1,4 МБ - 512 байт нулей.
KIV Да. Я ещё чуть-чуть поиграюсь и перейду. Я для себя даже составил документ с описанием некоторых структур (надеюсь он откроется у Вас в Ubuntu, лично мне показалось что у Вас Ubuntu). Вот сам файл: http://2u.ifolder.ru/19688339
Да. У меня Убунта. OpenOffice должен открыть docx. Максимум форматирование поедет, если оно будет слишком сложным. Я думаю у вас не такой документ.
Комментарии: В реальном режиме также доступны 32-битные регистры. А вот 64-битные только в длинном. А ещё проверяет сигнатуру 0x55,0xAA в конце. Иначе имеет полное право не загружать. И ещё в длинном режиме можно выполнять 32-битный код, если прыгнуть в 32-битный сегмент. Это и делают 32-битные ОС для запуска старых приложений. Кстати, посоветую не думать о сегментации и установить сразу два сегмента - код и данные с лимитом в 4 ГБ и всю защиту реализововать уже на уровне таблиц страниц. Потому что сегментация безнадёжно устарела. Насчёт lodsb: есть ещё целый набор инструкция для работы с массивами данных. Все они имеют постфикс размера - b (byte), w (word), d (dword), q (qword, только long mode). Вот они: lodsb (lodsw, lodsd, lodsq) - загрузка значения из DS:SI / ESI / RSI в регистр AL / AX / EAX / RAX. Увеличение DI на размер значения. stosb - Прямо противоположна. Сохранение AL в ESI cmpsb - Сравнение байта слова и т. п. из DS:SI и ESI и увеличение SI и DI. Результат помещается в FLAGS как при cmp. scasb - Сравнение байта ESI с AL. Результат помещается в FLAGS как при cmp.
KIV Ага. Вот только мне не хочется работать в режимах совместимости. Собственно заглянул я в структуру дескриптора для длинного режима: для пользовательских сегментов там большинство полей игнорируется. А базу и лимит, как я понял, можно вообще не указывать. Особое внимание там нужно уделить системным структурам (таблицам и прочему). Уровень привилегий устанавливается на страницу? А как вообще осуществляется многозадачность (не аппаратная)? Нужно ли для этого TSS? И как от одной задачи к другой переходить? По времени или ещё как-то? И меня очень сильно интересует такой вопрос. Современные процессоры многоядерные (два ядра, три, четыре...). И что это многоядерность нам даёт? Одновременно можно обработать несколько инструкций (одно ядро одну инструкцию в одной странице памяти, другое ядро - другую, в той же страницы памяти, или же в другой). И как тогда не запутаться где кто что делает? Как тогда защититься вообще от других процессоров? Да и что-то я не вижу в ассемблере средств для программирования отдельного процессора. Видимо это я тут фантастику наговорил. Может быть на самом деле эти ядра, это нечто другое. Выполняют они какую-нибудь другую функцию (скажем внутри процессора что-то перерабатывают). Какова правда программирования многоядерности?
С многоядерностью не работал - ничего точного не скажу. Без специальных действий дполнительные ядра отключены - можно не беспокоится за них. TSS в long mode нужен только для разделения стека ядра и приложения. Это обязательно. Иначе при прерывании на ring3 будет исключение. Программно многозадачность можно делать по разному. Я например делаю так: Code (Text): timer_int_handler: push rax rbx rcx rdx rsi rdi rbp r8 r9 r10 r11 r12 r13 r14 r15 [temp_page_flags] mov rdx, [current_thread] mov [rdx + THREAD.stack_pointer], rsp lea rax, [rdx + THREAD.fpu_state] fxsave [rax] @@: mov rdx, [rdx + THREAD.next] cmp [rdx + THREAD.suspend], FALSE jne @b mov rbx, [rdx + THREAD.process] cmp [rbx + THREAD.suspend], FALSE jne @b mov rax, [rbx + PROCESS.heap.page_dir] mov cr3, rax mov rsp, [rdx + THREAD.stack_pointer] mov [current_thread], rdx mov [current_process], rbx lea rax, [rdx + THREAD.fpu_state] fxrstor [rax] pop [temp_page_flags] pop r15 r14 r13 r12 r11 r10 r9 r8 rbp rdi rsi rdx rcx rbx invlpg [temp_page] mov dword[local_apic_regs + 0xB0], 0 pop rax iretq У меня простой циклический переключатель. Без приоритетов. При этом есть разделение адресных пространств и потокой одного процесса. PROCCESS и THREAD у меня структуры с описанием процессов и потокой соответсвенно. При создании потока надо заполнить его стек как будто произошло прерывание: 1) 16 нулей (регистры и атрибуты временной страницы) 2) Точка входа 3) Селектор сегмента кода 4) Флаги. Например, 0x202 5) Указатель стека приложения 6) Селектор сегмента данных (для стека) После mov cr3, rax у нас новый катлог страниц, а после mov rsp, ... у нас новый стек в котором сохранено состояние уже другого потока. Наверное непонятно объяснил...
KIV Очень ценная для меня информация! Спасибо! Так переключение задач происходим при каком событии? То есть при завершении задачи, при прохождении интервала в 10 миллисекунд, при вызове какого-то прерывания...?
По прерыванию от таймера (IRQ0). У меня - каждую 1 мс. Можно и реже. Это вытесняющая многозадачность. Она и в Linux И в Windows. Ещё есть кооперативная, когда приложения сами решают когда отдать управление следующей. Но такие ОС не предназдначены для декстопа.
А ещё такое: знаю что для программирования операционных систем используют C (именно C, а не C++). Сам как-то писал бинарный код на C (то ли через GCC) по статье (там потом нужно было получать бинарник, а это не так просто было в Windows). Данные располагались в самом верху, поэтому нужна была ассемблерная вставка для перехода на код. В общем стоит ли вообще использовать C для разработки? Просто я боюсь что много лишнего будет и не понятного кода.
KIV Если честно, то да. Но это только от того, что я пока совсем в этом не разбираюсь. Страшно становится только от того, чтобы для длинного режима нужно будет писать драйвер жёсткого диска (а то ведь странички нужно будет куда-то списывать).
Никак не могу найти где в Bochs, при отладке, можно указать точку останова для заданного адреса (то есть хочу проследить пошаговое выполнение именного моего кода с адреса 0x7C00). А то он начинает там всякие тесты или что-то ещё делать. Конечно я там нашёл что можно к следующей команде Step, либо через указанное количество команд Step XX, но так всё равно нужно выискивать свою команду, ждать.
Переключение задач, если оно не аппаратное, происходит тогда, когда это сделает переключалка. А когда она сделает, зависит от автора этой самой переключалки.
Собственно осталось для меня не раскрыто (либо не полностью раскрыто) несколько вопросов описанных выше. Приведу их ниже: ВОПРОС 1 ВОПРОС 2 ВОПРОС 3 ВОПРОС 4 ВОПРОС 5 Я конечно понимаю что Windows, Linux - это разное, но всё же есть мультисистемные программы...мне всё же интересно как лучше осуществить сборку проекта. То есть не просто взять один ASM-файл и в него навключать include. Ведь как-то делается по другому (возможно как в C). Каждый файл превращается в объектный, потом линкуется. Я не знаю. Вот и хочу посоветоваться, как удобнее вести проект и осуществлять сборку, какими программами.
lb адрес в вашем случае lb 0x7C00 это пишется в командной строке отладчика (такое текстовое поле внизу). На ассемблере тоже можно писать мультиситемные программы. Как? Посмотрите хотя бы fasm. Однако мультиархитектурыне программы - нет. То есть если вы написали 32-битное приложение для интеловской архитектуры и позаботились о мультиситемности, то оно у вас будет работать (после перекомпиляции) на всех системах на i386. А на ARM без эмулятора не получится. А на x86-64 получится через 32-битный кодовый сегмент. Если вам нужно, чтобы ваша ОС легко переносилась на другую архитектуру, то однозначно Си. В противном случае асм тоже подойдёт. Во-первых, А во-вторых, bochs понимает неполные образы дискет. То есть если дать ему 512 байт, то получится прочитать только первый сектор, а остальные будут "сбойными". Но вам то больше и не надо. Насчёт строковых команд: Я забыл ещё одну - movsb/movsw/movsd/modsq - копирование значения из DS:SI в ESI и увеличение SI и DI на его размер. Также к строковым командам можно добавлять префикс rep. Он выполняет следующую за ним строковую команду CX раз. На каждой итерации CX уменьшается на 1. Поэтому на выходе он обяхательно будет равен 0. Типичные применения rep: очистка массива: Code (Text): mov di, array mov cx, count xor ax, ax rep stosw копирование массива: Code (Text): mov si, array1 mov di, array2 mov cx, count rep movsw Есть ещё два префикса - repe и repne. У первого условие выхода из цикла помимо CX = 0 ещё и ZF = 0 (сравниваемые числа равны), а у второго ZF = 1 (сравниываемые числа не равны). Типичное применение repe и repne: Поиск заданного байта в массиве: Code (Text): mov di, array mov cx, count mov al, "!" ; Что искать repne scasb jne not_found di - 1 адрес этого байта Сравнение массивов: Code (Text): mov si, array1 mov di, array2 mov cx, count repe cmpsb jne .not_equals ; Массива равны, SI и DI указывают на конец массива + 1 Функция определения длины NULL-terminated строки ESI: Code (Text): str_len: mov cx, -1 ; Максимальное значение xor al, al repne scasb neg cx sub cx, 2 ret ; В CX длина строки не считая нуля на конце И наконец есть аппаратные циклы: Code (Text): mov cx, 256 @@: mov ah, 0x0E mov al, "!" int 0x10 loop @b ; Этот код выводит восклицательный знак 256 раз loop уменьшает CX (ECX/RCX) на 1 и если он после этого не равен нулю передаёт управление на метку указанную в качестве операнда. Важно: в отличии от строковых операций loop сначала сравнивает, а потом уменьшает, поэтому cx = 0 приведёт к повторению 65536 раз, а в 64-битном режиме - аж 2 ^ 64 раз! Если вы знаете, что может получится 0, то делайте дополнительную проверку до цикла и перепрыгивайте цикл при CX = 0. А rep при CX = 0 просто не разу не выполняет команду, поэтому дополнительных проверок не требует. И ещё - loop может переходить на +-128 байт. Если цикл больше, то придётся заменить loop на: Code (Text): dec cx jnz .loop
Программы которые после перекомпиляции смогут работать и на i386 и на amd64 и на ARM и на PowerPC. На асме такое не написать. Но не всегда это и нужно. И посмотрите моё предыдущее сообщение я его немножко дополнил пока вы писали ответ.
Поскольку у меня есть своя ФС я написал программу на Си, которая делает образ диска из папки. Причём ещё и ставит в начало образа указанный начальный загрузчик. У меня вот акой шел-скрипт (в винде - bat-файл) В папке disk у меня файлы, которые должны попасть на диск. Некоторые пользуюся не программами, а пишут специальные макросы fasm и описывают файловую систему в ещё одном исходнике.