Доброго времени суток!
Пишу загрузчик на языке GNU Assembler-а. Загрузчик своеобразный... Его основная работа - загрузить оригинальный загрузчик и передать ему управление. Для этого, я подменяю оригинальный загрузчик своим (записываю в первый сектор раздела), а оригинальный сохраняю в другом секторе.
При этом мой загрузчик делает следующие действия:
- После загрузки в оперативную память по адресу 0x7C00 он копирует себя же в другое место (например, по адресу 0xA000)
- Далее прыгает на этот адрес со смещением (чтобы не выполнять тот же код) и продолжает работу оттуда
- Читает раздел с оригинальным загрузчиком в память по адресу 0x7C00
- Прыгает по адресу 0x7C00 и передаёт управление оригинальному загрузчику
Проблемы возникают при работе с адресами.
Есть фишка, что адрес рассчитывается относительно соответствующего сегментного регистра по формуле:
реальный_адрес = (сегментный_регистр << 4) + относительный_адрес
Но когда эта формула срабатывает - я не всегда могу понять.
Например, копирование блоков памяти. Вот как я копирую свой загруженный в оперативную память загрузчик.
movw $0x0, %si # откуда = DS:SI = (0x07C0 << 4) + 0x0 = 0x7C00
movw $0x0A00, %ax
movw %ax, %es # ES = 0x0A00
movw $0x0, %di # куда ES:DI = (0x0A00 << 4) + 0x0 = 0xA000
movw $0x0200, %cx # сколько = 512
rep movsb
По комментариям видно, что я предполагаю, что как адрес «откуда», так и адрес «куда» рассчитывается именно с учётом сдвига значения сегментного регистра.
Далее по команде jmp. Если я не ошибаюсь, то при передаче ей в качестве аргумента имени метки, итоговый адрес перехода рассчитывается как раз относительно значения сегментного регистра кода cs. Тогда, если я обнулю этот регистр, то по сути я могу передать команде в качестве аргумента абсолютный адрес:
movw $0x0, %ax
movw %ax, %cs
jmp 0xA000
Но это не работает. Более того, для меня стало неожиданным, что сработал код:
jmp 0x0A00
По крайней мере сообщение, выводимое сразу после установки всех сегментных регистров, выводится два раза:
_boot:
# Устанавливаем начальное значение сегментных регистров
cli
movb %dl, iBootDrive
movw $0x07C0, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
movw %ax, %sp
sti
mWriteString msgHello
Кроме этого, меня интересует, как пропустить уже выполненный код. Логичным для меня является что-то вроде этого:
jmp 0xA000 + continue
continue:
То есть выполняется прыжок в адрес, равный 0xA000 (там скопированный загрузчик) + значение метки. Вроде бы всё логично, но следующий код опять же просто выводит два сообщения:
jmp 0x0A00 + continue
continue:
Как будто просто игнорирует смещение метки и выполняет код заново.
Плюс ко всему, инициализация сегментного регистра кода cs вообще ломает исполнение кода. Даже элементарный вывод строки не может выполниться.
Возможно, следующая информация будет полезна. Собираю загрузчик так:
as boot.s -o boot.o
ld -Ttext 0x0000 --oformat=binary boot.o -o boot.bin
Директиву org не указываю.
Таким образом, у меня вопрос: Как правильно реализовать копирование памяти и передачу туда управления?
Спасибо!