Я пытаюсь написать свою мини ОС. Я учусь на других проектах, поэтому я действительно понятия не имею, подходит ли мой код для загрузчика и ядра. Я использую сборку 16 бит, NASM и загружаю загрузчик и ядро в виде файлов .bin на образ дискеты. Я запускаю ОС с qemu. Я дошел до того, что добавление дополнительных строк кода стирает отображаемые строки, которые появляются при запуске ОС. Например, если я запускаю ОС и появляется сообщение «Добро пожаловать в мою ОС!», после добавления некоторых строк кода это будет выглядеть так: «Добро пожаловать в m» (конец исчезает).
вот мой код:
bootloader.asm
[bits 16]
[org 0x7c00]
; Use the boot drive number passed to us by BIOS in register DL
start:
xor ax,ax ; We want a segment of 0 for DS
mov ds,ax ; Set AX to appropriate segment value
mov es,ax ; In this case we'll default to ES=DS
mov bx,0x8000 ; Stack segment can be any usable memory
mov ss,bx ; This places it with the top of the stack @ 0x80000.
mov sp,ax ; Set SP=0 so the bottom of stack will be @ 0x8FFFF
cld ; Set the direction flag to be positive direction
mov si, welcome_msg
call print_string
mov si, kernel_load
call print_string
pushf
stc
mov ah,00h ; Reset Disk Drive
int 13h
read_sector:
mov ax, 0x0
mov es, ax ; ES = 0
mov bx, 0x1000 ; BX = 0x1000. ES:BX=0x0:0x1000
; ES:BX = starting address to read sector(s) into
mov ah, 02 ; Int 13h/AH=2 = Read Sectors From Drive
mov al, 01 ; Sectors to read = 1
mov ch, 00 ; CH=Cylinder. Second sector of disk
; is at Cylinder 0 not 1
mov cl, 02 ; Sector to read = 2
mov dh, 00 ; Head to read = 0
; DL hasn't been destroyed by our bootloader code and still
; contains boot drive # passed to our bootloader by the BIOS
int 13h ; Read Sectors From Drive
jc error_kernel_load ; error loading kernel
popf
jmp 0x0:0x1000 ; jmp to kernel offset
cli ; Disable interrupts to circumvent bug on early 8088 CPUs
hlt ; halts the central processing unit (CPU) until the next external interrupt
error_kernel_load:
mov si, error_msg
call print_string
mov si, restart_msg
call print_string
mov ah,00 ; wait for key press
int 16h
xor ax,ax
int 19h ; reboot the computer
print_string: ; Routine: output string in SI to screen
lodsb ; Get character from string
or al,al
jz exit
mov ah,0x0e
int 10h ; int 10h 'print char' function
jmp print_string
exit:
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Messages to print
welcome_msg db 'Welcome to Bootloader!!!',0x0D,0x0A,0
kernel_load db 'Loading kernel....',0x0D,0x0A,0
error_msg db 'Kernel.bin not found!',0x0D,0x0A,0
restart_msg db 'Press any key to restart..',0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
times 510-($-$$) db 0 ; Create padding to fill out to 510 bytes
dw 0xaa55 ; Magic number in the trailer of a boot secto
kernel.asm
[bits 16]
[org 0x1000]
section .data:
cursor_col: db 0 ; cursor column
cursor_row: db 0 ; cursor row
line_col: dw 0 ; line column
line_row: dw 0 ; line row
color: db 3Fh ; background and forground color (at start set random the formal way > number+letter)
mode: db 0
start:
mov ax, 07C0h ; Set up 4K stack space after this bootloader
add ax, 288 ; (4096 + 512) / 16 bytes per paragraph
mov ss, ax
mov sp, 4096
mov byte [color], 3Fh
call clear_screen ; clear the screen and color it
; VVV print "Minerald welcome" message VVV
mov byte [cursor_col], 08
mov byte [cursor_row], 28
call set_cursor ; print at the requested position of the screen
mov si, welcome_string ; Put string position into SI
call print_string ; Call our string-printing routine
; VVV print "Press Space" message VVV
mov byte [cursor_col], 14
mov byte [cursor_row], 26
call set_cursor ; print at the requested position of the screen
mov si, press_key_string ; Put string position into SI
call print_string ; Call our string-printing routine
mov ah, 0h ; wait for key press
int 16h
mov byte [color], 1Fh
call clear_screen ; clear the screen and color it
mov byte [cursor_col], 2
mov byte [cursor_row], 0
call set_cursor ; print at the requested position of the screen
mov si, basic_background ; Put string position into SI
call print_string
mov byte [cursor_col], 22
mov byte [cursor_row], 0
call set_cursor ; print at the requested position of the screen
mov si, basic_background ; Put string position into SI
call print_string ; Call our string-printing routine
mov ah, 0 ; set display mode function.
mov al, 13h ; mode 13h = 320x200 pixels, 256 colors.
int 10h ; set it!
;================================= ; rectangles
mov byte [line_col], 80
mov byte [line_row], 165
call print_rectangle ; print rectangle
mov byte [line_col], 80
mov byte [line_row], 135
call print_rectangle ; print rectangle
;==================================
end:
jmp $ ; Jump here - infinite loop!
;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;
clear_screen:
mov ah, 06h ; Scroll up function
xor al, al ; Clear entire screen
xor cx, cx ; Upper left corner CH=row, CL=column
mov dx, 184Fh ; lower right corner DH=row, DL=column
mov bh, byte [color] ; set background and foreground color
int 10h
ret
;;;;;;;;;;
;;;;;;;;;;
print_string: ; Routine: output string in SI to screen
lodsb ; Get character from string
or al,al
jz exit
mov ah,0x0e ; int 10h 'print char' function
int 10h
jmp print_string
exit:
ret
;;;;;;;;;;
;;;;;;;;;;
set_cursor:
mov dh, byte [cursor_col] ; cursor col
mov dl, byte [cursor_row] ; cursor row
mov ah, 02h ; move cursor to the right place
xor bh, bh ; video page 0
int 10h ; call bios service
ret
;;;;;;;;;;
;;;;;;;;;;
print_rectangle:
%INCLUDE "print_rectangle.asm"
;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Messages to print
welcome_string db 'Welcome to my OS!', 0
press_key_string db 'Press any key to continue...', 0
enter_message_string db 'Enter your message below',0
APM_ERROR_string db 'APM Error...'
basic_background db '--------------------------------------------------------------------------------'
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;END OF KERNEL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
это команды загрузки:
nasm -f bin -o kernel.bin kernel.asm
nasm -f bin -o bootloader.bin bootloader.asm
dd if=bootloader.bin of=floppy.flp bs=512 seek=0 conv=notrunc
dd if=kernel.bin of=floppy.flp bs=512 seek=1 conv=notrunc
qemu-system-i386 -fda floppy.flp
изменить:
из списка файлов nasm до усечения сообщений:
300 ; Messages to print
301 00000191 57656C636F6D652074- welcome_string db 'Welcome to my OS!', 0
302 0000019A 6F204D696E6572616C-
303 000001A3 644F532100
304 000001A8 507265737320616E79- press_key_string db 'Press any key to continue...', 0
305 000001B1 206B657920746F2063-
306 000001BA 6F6E74696E75652E2E-
307 000001C3 2E00
308 000001C5 456E74657220796F75- enter_message_string db 'Enter your message below',0
309 000001CE 72206D657373616765-
310 000001D7 2062656C6F7700
311 000001DE 41504D204572726F72- APM_ERROR_string db 'APM Error...'
312 000001E7 2E2E2E
313 000001EA 2D2D2D2D2D2D2D2D2D- basic_background db '--------------------------------------------------------------------------------'
из списка файлов nasm после усечения сообщений:
332 ; Messages to print
333 000001E8 57656C636F6D652074- welcome_string db 'Welcome to MineraldOS!', 0
334 000001F1 6F204D696E6572616C-
335 000001FA 644F532100
336 000001FF 507265737320616E79- press_key_string db 'Press any key to continue...', 0
337 00000208 206B657920746F2063-
338 00000211 6F6E74696E75652E2E-
339 0000021A 2E00
340 0000021C 456E74657220796F75- enter_message_string db 'Enter your message below',0
341 00000225 72206D657373616765-
342 0000022E 2062656C6F7700
343 00000235 41504D204572726F72- APM_ERROR_string db 'APM Error...'
344 0000023E 2E2E2E
345 00000241 2D2D2D2D2D2D2D2D2D- basic_background db '--------------------------------------------------------------------------------'
346 0000024A 2D2D2D2D2D2D2D2D2D-
Можете ли вы добавить строки с сообщениями из файла листинга NASM? (опция
-l kernel.lst
и скопируйте всего несколько строк вокругwelcome_string
) - чтобы увидеть, как он компилируется и по какому смещению они размещаются.@ Ped7g Я отредактировал вопрос.
Это из версии, которая показывает уже усеченные строки, или из версии, которая работает? Я немного боялся, что вы пересекаете границу сектора 512 байт, но в листинге этого не указано, последнее смещение 0x1EA все еще находится в пределах 512 байт, так что, вероятно, это что-то в коде (которого я еще не читал).
В коде, возможно, это не связано с вашей ошибкой, но ваше использование службы
int 10,0E
неверно, вы не установилиBH
на текущую страницу, и мне кажется, что все еще может быть какое-то значение из чтения сектора, вероятно,0x10
, что, вероятно, заставляет его работать случайно, но я не хочу глубоко копаться в коде, который написан небрежно и, вероятно, «проверен» просто путем его выполнения и проверки вывода. Прекратите это делать, запустите его в отладчике, пошагово выполняя каждую инструкцию, и проверьте все предположения/эффекты инструкций, не полагайтесь на вывод.На самом деле еще одна важная вещь... проверьте список kernel.lst в начале, я не думаю, что директива
section
актуальна, поскольку вы используете форматbin
для вывода, поэтому ваше ядро, скорее всего, начинается с0000:1000
с байтами данных, а не с инструкциями, что, вероятно, является еще одной чистой удачей, что вы получаете даже несколько значимый вывод после выполнения данных. Смешиваниеsection
иorg
не имеет смысла, либо вы используетеsection
и более сложный формат исполняемого файла, где компоновщик будет сортировать расположение памяти в соответствии с предоставленным сценарием компоновщика, либо вы используете двоичный файл, где вам нужно разместить себя .@Ped7g Спасибо за ваши комментарии. Я новичок в этом, и я все еще учусь. Я отредактировал сообщение с файлом листинга после добавления строк кода, предыдущее было до того, как сообщения были усечены. Если я действительно пересечу 512B, что мне делать? загрузить другой сектор? и еще один момент, я тоже новичок в linux, и я не мог понять, как отлаживать сборку с помощью gdb, у вас есть какие-либо предложения?
И я не совсем понял часть "раздел" и "орг". Не могли бы вы объяснить, что такое раздел и что мне делать? Просто стереть метку раздела .data?
Если вы пересекаете границу сектора 512B, тогда да, вам нужно загрузить более 1 сектора (вы должны загрузить (size_of_kernel/512) округленное количество секторов). Вы запускаете свой код загрузчика внутри виртуальной машины QEMU, поэтому вам нужен отладчик, способный отлаживать код виртуальной машины, это не так тривиально, как отладка обычного кода приложения, работающего под вашей хост-ОС (и это не тривиально), поэтому я хмурюсь, когда вижу, что люди, которые плохо знакомы с ассемблером, начинают изучать загрузчик и программирование ядра вместо того, чтобы сначала изучать основы ассемблера, потому что это похоже на утроение размера проблем при запуске.
И я не занимаюсь разработкой ОС, поэтому я не знаю, как отлаживать код, работающий внутри QEMU, из других вопросов по разработке ОС здесь похоже, что запуск загрузчиков с симулятором BOCHS немного более удобен для отладки, хотя я сам не смог настроить его за 5 минут я выделил на пробу. ...о разделе...взгляните на это с другой точки зрения, вы загружаете байтовые данные "машинный код" с диска в память, затем выполняете его (без какой-либо постобработки загруженных данных), поэтому все инструкции и адреса уже определены в двоичных данных. Директива
org
регулирует внутренние смещения во время сборки.С другими исполняемыми форматами, такими как
ELF
(32 или 64 бит), процесс сборки создаст первые объектные файлы, которые имеют только относительные локальные смещения в объектном файле, а не окончательные адреса,org
вообще не используется. Затем объектные файлы собираются компоновщиком, и из них собирается окончательный исполняемый файл, соединяя различные символы между разными объектными файлами, а также помещая данные и код в [отдельные иногда] области, где они принадлежат в соответствии со сценарием компоновщика, используемым для связывания. или часть кода определила целевой раздел директивойsection
, которая предназначена для компоновщика.Когда вы создаете двоичный файл, то, что вы видите в листинге:
line_number address(offset) machine_code_bytes
буквально то, что у вас есть в файле .bin (ну, только часть машинного кода, номера строк и смещения - это просто информация для вас, откуда машина код был скомпилирован, и куда он приземлится) ... у вас нашkernel.bin
начинается с нескольких нулей, созданныхdb 0
, и вы будете в загрузчикеjmp 0:0x1000
в этих байтах данных и выполните их как инструкции. (поскольку для процессора байт памяти - это байт памяти, он не может сказать, что такое код, а что данные, нет никакой разницы).А в реальном режиме 16b машинный код
00 00
является кодом операции инструкцииadd [bx+si],al
... и т. д., так что это то, что вы выполняете. ("db" - это не инструкция, это директива для генерации значений байтов ассемблером, поэтому, когда вам нужен какой-то байт в машинном коде со значением13
, вам не нужно вспоминать, какая инструкция имеет код операции, начинающийся с13
, но вы можете написатьdb 13
... и наоборот, мнемоника инструкций поможет вам не вспоминать, чтоret
этоdb 195
(хотя это сработает, для большинства людей будет слишком много запоминать кодировки всех инструкций и запишите их цифрами).Спасибо за уделенное время. Я все еще учусь в старшей школе, и раньше я программировал на ассемблере [для Windows], но, по-видимому, я все еще не на 100% владею всеми основами. Последний комментарий о том, что вы можете использовать «db 195» вместо «ret», для меня нов и довольно интересен. Очень помогло, большое спасибо :)
Хм, почитайте что-нибудь о принципах/архитектуре компьютеров, о том, что такое "машинный код" и как кодируются инструкции по сборке... и еще раз взгляните на файл листинга и сравните его, просматривая содержимое
kernel.bin
(в linux install "hd " = hexdump? В Ubuntu вы можете найти его в пакете "bsdmainutils"), чтобы увидеть, как этот машинный код в листинге является единственным содержимым, которое заканчивается в файле "bin", и это то, что процессор видит + выполняет (но в " двоичный», так как ЦП работает только с битами — т. е. уровнями электрического тока в ячейках, которые обрабатываются как логические значения 0 или 1).Может показаться, что все немного сложно и странно, но постарайтесь подумать об этом больше с точки зрения проектировщика HW, как вы все время работаете с электрическими токами 0/1, и как все спроектировано так, чтобы быть на самом деле глупо-простым. , то есть содержимое памяти не имеет каких-либо флагов, если оно хранит инструкции, целые числа или числа с плавающей запятой, это было бы слишком много возни с транзисторами и т. д. Вскоре вся машина и сборка представляют собой на самом деле очень простой переросший детерминированный калькулятор, меняющий свое состояние каждый такт.
Тогда многое из этого «странного ассемблера» будет иметь гораздо больше смысла, поскольку ассемблер - это не столько язык программирования (логический, ориентированный на человека), сколько языки высокого уровня, поскольку он больше отражает аппаратную конструкцию ЦП, так что это логично с точки зрения размещения миллионов транзисторов на небольшом чипе, «логика программирования» является вторичной в этом процессе, позволяя создавать приложения с помощью ЦП, но не беспокоясь о том, что какая-то конкретная инструкция ведет себя «причудливо» с точки зрения человеческой логики. вид, если дизайн HW таким образом является более элегантным и производительным.
Все, что вы сказали, имеет большой смысл, я почитаю больше об архитектуре :)