Hello World загрузчик не работает - PullRequest
5 голосов
/ 13 апреля 2010

Я работал над учебными пособиями на этой веб-странице , которая постепенно создает загрузчик, отображающий Hello World.

2-й урок (где мы пытаемся получить «А» для вывода) работает отлично, а 1-й у меня вообще не работает! (BIOS полностью игнорирует дискету и загружается прямо в Windows). Это не проблема, хотя любые объяснения приветствуются.

Настоящая проблема в том, что я не могу заставить 3-й учебник работать. Вместо вывода «Hello World» я получаю необычный символ (и мигающий курсор) в левом нижнем углу экрана. Это выглядит как смайлик внутри скругленного прямоугольника. Кто-нибудь знает, как заставить Hello World отображать как надо?

Ответы [ 2 ]

7 голосов
/ 13 апреля 2010

Вы говорите «загрузитесь прямо в Windows», поэтому я предполагаю, что вы используете физический ПК. Замечание на будущее: всегда используйте эмулятор для разработки! Это просто проще Мне нравится Bochs для OSDeving, потому что он имеет хорошие функции отладки. Теперь о возможном решении.

Существует множество ошибочных BIOS, которые нарушают неофициальные спецификации IBM PC для адреса загрузки 0x7C00.

Это может вызвать много проблем с адресами памяти и т. Д. При сборке. Сделайте так, чтобы начало выглядело так:

[BITS 16] ;tell the assembler that its a 16 bit code
[ORG 0x7C00] ;this tells the assembler where the code will be loaded at when it runs on your machine. It uses this to compute the absolute addresses of labels and such.

jmp word 0:flush ;#FAR jump so that you set CS to 0. (the first argument is what segment to jump to. The argument(after the `:`) is what offset to jump to)
;# Without the far jmp, CS could be `0x7C0` or something similar, which will means that where the assembler thinks the code is loaded and where your computer loaded the code is different. Which in turn messes up the absolute addresses of labels.
flush: ;#We go to here, but we do it ABSOLUTE. So with this, we can reset the segment and offset of where our code is loaded.
mov BP,0 ;#use BP as a temp register
mov DS,BP ;#can not assign segment registers a literal number. You have to assign to a register first.
mov ES,BP ;#do the same here too
;#without setting DS and ES, they could have been loaded with the old 0x7C0, which would mess up absolute address calculations for data. 

Видите, некоторая нагрузка на 0x07C0:0000 и большая нагрузка (и считается, что она соответствует) на 0x0000:7C00. Это один и тот же плоский адрес, но разные настройки сегмента действительно могут испортить абсолютные адреса памяти. Итак, давайте удалим «магию» ассемблера и посмотрим, как это выглядит (обратите внимание, я не гарантирую, что адреса будут полностью корректными с этим. Я не знаю размер всех кодов операций)

jmp word 0:0x7C04 ;# 0x7C04 is the address of the `flush` label 
...

Итак, мы переходим к абсолютному адресу.

Теперь тогда. Что происходит, когда мы этого не делаем?

возьмем эту программу, например:

mov ax,[mydata]
hlt

mydata: dw 500 ;#just some data

Это разбирает на что-то вроде

mov ax,[0x7C06] 

О, ну, это использует абсолютную адресацию, так как это может пойти не так? Ну, а если DS на самом деле 0x7C0? тогда вместо ожидаемого ассемблера 0:0x7C06 он получит 0x7C0:0x7C06, которые не того же плоского адреса.

Надеюсь, это поможет вам понять. Это действительно сложная тема, и для ее полного понимания требуется некоторое время программирования низкого уровня.

1 голос
/ 13 апреля 2010

Я думаю, что проблема, вероятно, связана с указанным происхождением.

[ORG 0x7C00]    ;Origin, tell the assembler that where the code will

Исходя из нашего разговора, выясняется, что адрес не так предсказуем. Возможно, просто то, что DS регистр сегмента данных не то, что вы ожидаете. На самом деле вы можете получить исходный листинг с веб-страницы для работы, добавив push и pop of ds перед вызовом, чтобы отобразить строку, подобную этой,

 push cs
 pop ds

Если не работает следующий код.

 [ORG 0x000]    ; switched to 0 since we are going to try to correct it ourself

 call nextinstruction
 nextinstruction:    ; get the return address of the call into dx
 pop dx              ; which is essentially the start of the code + 3 (3 bytes for the call instruction)
 MOV SI, HelloString ;Store string pointer to SI
 add si, dx          ; add IP from start of program
 sub si, 3           ; subtract the 3 the call instruction probably took
 push cs
 pop ds              ; make ds the same as cs.  
 CALL PrintString   ;Call print string procedure
 JMP $      ;Infinite loop, hang it here.

Этот код вычисляет смещение во время выполнения исполняемого кода, а также гарантирует, что DS указывает на тот же сегмент. Если не указано иное, инструкции с участием SI обычно также используют DS в качестве сегмента кода для ссылки на память.

DS - это сегментный регистр, и вы можете прочитать что-то вроде Art of Assembly , чтобы узнать больше.

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

...