Ядро не загружается по правильному адресу
Хорошо, я вырвал оставшиеся волосы у меня с этой проблемой...
По сути, я пытаюсь разработать очень простую хобби ОС. Операционная система будет работать на платформе X86 без дискеты FAT12. Прежде чем протестировать его на своем компьютере, я создаю образ диска для тестирования с помощью Bochs 2.6.2.
Как обычно, я поместил загрузчик на загрузочный сектор образа диска, затем добавил образ ядра (KERNEL.SYS) в виде обычного файла FAT12.
Загрузчик предназначен для поиска KERNEL.SYS, загрузки его по адресу 1000h:0000 и перехода к нему.
Однако, когда я тестирую образ диска с помощью Bochs, я получаю следующие результаты (начиная с перехода к 07C0:0000):
Насколько я знаю, я знаю, что Bochs либо продолжает сбрасывать (тройной сбой?), Либо возвращается обратно в 0000:0000(A20 не включен). Есть бесконечный цикл (JMP $
) при запуске ядра (для целей тестирования), поэтому я знаю, что оно не выполняется. Я также знаю, что 1000:000 не превышает 1 МБ, поэтому я не уверен.
Это та часть, которая действительно беспокоит меня: когда я получил дамп памяти от Bochs (32 МБ), я вижу, что KERNEL.SYS был загружен в 0980:0000 или 0000:9800. Я точно знаю, что никогда ничего там не загружал, так что происходит?
Код загрузчика:
[BITS 16]
[ORG 0x00]
JMP btldr_init
NOP
OEMName DB 'MAGMAPRE'
BytesPerSector DW 0x0200
SectorsPerCluster DB 0x01
ReservedSectors DW 0x0001
NumberOfFATS DB 0x02
MaxRDirEntries DW 0x00E0
TotalSectors DW 0x0B40
MediaDescriptor DB 0xF0
SectorsPerFAT DW 0x0009
SectorsPerTrack DW 0x0012
NumberOfHeads DW 0x0002
HiddenSectors DD 0x00000000
TotalSectorsL DD 0x00000000
DriveNumber DW 0x0000
Reserved DB 0x00
VolumeID DD 0xF00DCAFE
VolumeName DB 'MAGMA DISK '
FileSysID DB 'FAT12 '
btldr_init:
MOV AX, 0x07C0
MOV DS, AX
MOV ES, AX
MOV SS, AX
MOV SP, 0x7C00
MOV DI, 0x03
read_rdir:
MOV [driveNo], DL
MOV AX, 0x13
CALL dos_to_bios
MOV AX, 0x020E
MOV BX, buffer
CALL reset_dsksys
STC
INT 0x13
JNC search_rdir
DEC DI
JZ fatal_error
JMP read_rdir
search_rdir:
MOV DI, buffer
MOV SI, knlName
MOV DX, [MaxRDirEntries]
.compare:
PUSH DI
PUSH SI
CLD
MOV CX, 0x0B
REP CMPSB
JE load_disk_fath
POP SI
POP DI
.next_entry:
DEC DX
JZ fatal_error
ADD DI, 0x20
JMP .compare
load_disk_fath:
POP SI
POP DI
MOV AX, word [DS:DI+0x1A]
MOV [cluster], AX
MOV AX, word [DS:DI+0x1C]
MOV [knlSize], AX
MOV DI, 0x03
load_disk_fat:
MOV AX, [ReservedSectors]
CALL dos_to_bios
MOV AX, 0x0209
MOV BX, buffer
STC
INT 0x13
JNC load_file_clusterh
DEC DI
JZ fatal_error
JMP load_disk_fat
load_file_clusterh:
MOV DI, 0x03
MOV AX, 0x1000
MOV ES, AX
load_file_cluster:
MOV AX, [cluster]
ADD AX, 0x21
CALL dos_to_bios
MOV AX, 0x0201
MOV BX, [bufPos]
CMP BX, [knlSize]
JAE get_info
STC
INT 0x13
JC .try_again
MOV BX, [bufPos]
ADD BX, 0x200
MOV [bufPos], BX
JMP get_next_cluster
.try_again:
DEC DI
JZ fatal_error
JMP load_file_cluster
get_next_cluster:
MOV AX, [cluster]
MOV BX, [cluster]
SHR BX, 0x01
ADD BX, AX
MOV DX, [DS:BX+buffer]
TEST BX, 0x01
JNZ .even
.odd:
SHR DX, 0x04
JMP .check
.even:
AND DX, 0x0FFF
.check:
CMP DX, 0xFF0
JE get_info
MOV [cluster], DX
;JMP load_file_cluster
get_info:
MOV AX, 0x0BE0
MOV ES, AX
MOV DI, 0x02
XOR SI, SI
.low_mem:
XOR AX, AX
INT 0x12
JC .low_mem_err
TEST AX, AX
JZ .low_mem_err
.low_mem_success:
MOV [ES:DI], AX
JMP .upper_memE801
.low_mem_err:
XOR DI, DI
OR SI, 0x0001
MOV [ES:DI], SI
.upper_memE801:
XOR CX, CX
XOR DX, DX
MOV AX, 0xE801
INT 0x15
JC SHORT .upper_mem_err
CMP AH, 0x86
JE SHORT .upper_mem_err
CMP AH, 0x80
JE SHORT .upper_mem_err
JCXZ .useax
MOV AX, CX
MOV BX, DX
JMP .useax
.upper_mem_err:
JMP .upper_mem88
.useax:
MOV DI, 0x04
MOV [ES:DI], AX
ADD DI, 0x02
MOV [ES:DI], BX
JMP goto_kernel
.upper_mem88:
MOV AH, 0x88
INT 0x15
JC SHORT .upper_mem_err88
TEST AX, AX
JE SHORT .upper_mem_err88
CMP AH, 0x86
JE SHORT .upper_mem_err88
CMP AH, 0x80
JE SHORT .upper_mem_err88
.success:
MOV DI, 0x08
MOV [ES:DI], AX
JMP goto_kernel
.upper_mem_err88:
OR SI, 0x0002
goto_kernel:
JMP 1000h:0000
reset_dsksys:
PUSHA
XOR AX, AX
MOV DL, [driveNo]
INT 0x13
JC fatal_error
POPA
RET
fatal_error:
.repeat:
MOV AL, [DS:SI]
OR AL, AL
JZ .end
MOV AH, 0x0E
INT 0x10
INC SI
JMP .repeat
.end:
CLI
HLT
dos_to_bios:
PUSH BX
PUSH AX
MOV BX, AX ; SAVE LOGICAL SECTOR
MOV DX, 0 ; FIRST THE SECTOR
DIV WORD [SectorsPerTrack]
ADD DL, 01H ; PHYSICAL SECTORS START AT 1
MOV CL, DL ; SECTORS BELONG IN CL FOR INT 13H
MOV AX, BX
MOV DX, 0 ; NOW CALCULATE THE HEAD
DIV WORD [SectorsPerTrack]
MOV DX, 0
DIV WORD [NumberOfHeads]
MOV DH, DL ; HEAD/SIDE
MOV CH, AL ; TRACK
POP AX
POP BX
MOV DL, BYTE [driveNo] ; SET CORRECT DEVICE
RET
knlName DB 'KERNEL SYS'
knlSize DW 0x0000
err DB 'Could not load Magma.', 0x00
driveNo DB 0x00
cluster DW 0x0000
cOffset DW 0x0000
bufPos DW 0x0000
TIMES 510 - ($ - $$) DB 0x00
DB 0x55
DB 0xAA
buffer:
2 ответа
Одна вещь, которая кажется неприличной в вашем коде, это то, что вы устанавливаете сегмент стека на 07C0, а затем указатель стека на 7C00, так что стек расположен в 07C0:7C00 или 0000:F800, что не является причиной сбоя AFAIK. Однако следует учитывать, что загрузочная запись может быть загружена либо в 0000:7C00, либо в 07C0:0000 в зависимости от BIOS, и было бы неплохо нормализовать сегмент кода с помощью дальнего перехода, просто чтобы быть в безопасности, также помните, что это не имеет значения, если вы создаете его в 07C0:0000 или 0000:7C00, если это не противоречит.
Как упоминалось ранее, вам не нужно STC до INT 13h, и вы можете опустить их, чтобы сэкономить место, еще лучше, если вы можете очистить вещи, вызвав подпрограмму, которая сбрасывает диск, преобразует LBA в CHS и считывает сектора целиком кода, который имеет код чтения диска, разбросанный по всей остальной части кода. Также при сбросе диска он устанавливает флаг переноса, если накопитель не готов, и совершенно безопасно продолжать вызывать INT 13h AX=0, пока флаг переноса не сбросится.
Если вы посмотрите на адрес, по которому вы утверждаете, что ядро было загружено, преобразуйте его в абсолютный адрес, а затем вернитесь к смещению сегмента с сегментом 07C00h, в котором вы загрузили ядро в 07C0:1C00, что может иметь больше смысла, если проблема заключается в сегментации.
Что ж, похоже, все в порядке, так что проблема может быть где-то еще. (Кстати, STC не требуется до 13 часов, но это не может быть причиной описанной проблемы).
Итак, я бы предложил попробовать использовать отладчик Bochs и вставить точки останова int3 на ключевые позиции в вашем источнике - например, перед операциями чтения ядра и непосредственно перед дальним переходом (где вы можете проверить, что загружено на $1000:0000
Еще одно замечание, также не очень важное, это значение SP. 7c00h как-то не обычное значение для SP, но в любом случае это не очевидная проблема.
Кроме того, код ядра должен быть проверен еще раз.