use16 ; указываем ассемблеру, что наш исполняемый код будет 16 битный
org 7c00h
start:
cli
mov ax, cs
mov ds, ax ; data segment
mov ss, ax ; stack segment
mov es, ax ; enhanced segment
mov sp, start ; stack pointer
mov [disk_start], dl ; обычно при запуске системы номер диска, с которого проведён запуск передаётся в dl
sti
call .IP ; сохраняем в стек наш живой адрес с которого мы запустились
.IP:
xor dx, dx ; координаты курсора X = 0 и Y = 0
mov ax, msg_start ; сообщаем, что загрузчик загружен
call PRINT_STRING
inc dh ; Y + 1
xor dl, dl ; X = 0
mov ax, msg_segment ; выводим сегмент загрузки (просто для информации)
call PRINT_STRING
mov ax, cs
call PRINT_HEX_WORD ; выводим сегмент
inc dh ; Y + 1
xor dl, dl ; X = 0
mov ax, msg_adress ; информация — с какого адреса стартовали
call PRINT_STRING
pop ax
sub ax, 14h
call PRINT_HEX_WORD ; выводим байт код диска
inc dh ; Y + 1
xor dl, dl ; X = 0
mov ax, msg_disk_start ; информация — с какого номера диска стартовали
call PRINT_STRING
mov al, [disk_start]
call PRINT_HEX_BYTE ; выводим байт код диска
; зафиксируем сообщения перед загрузкой второго сектора
inc dh ; Y + 1
xor dl, dl
mov ax, msg_press_key ; адрес строки — нажмите клавишу
call PRINT_STRING
call PRESS_KEY
; загружаем второй сектор
push ax
push dx ; сохраняем координаты x, y
mov ah, 02h ; функция читать секторы int 13h
mov dl, [disk_start] ; номер диска (0=диск A…; 80h=тв.диск 0, 81h=тв.диск 1 …)
mov dh, 0 ; номер головки чтения
mov ch, 0 ; номер дорожки (цилиндра) от 0 до n
mov cl, 2 ; номер сектора, 1 — n
mov al, 1 ; число секторов (в сумме не больше чем один цилиндр)
mov bx, 600h ; адрес = 32768. Посчитал как 7C00h + 512 (загруженный сектор) + 512 (резерв), es уже равен cs (буфер ES:BX)
int 13h ; вызываем функцию. Если CF=1(флаг переноса), то ошибка и в ah — код ошибки
pop dx ; востанавливаем координаты x, y
jc .ERROR ; если ошибка то преходим и показываем её код
pop ax
inc dh ; Y + 1, новая строка
xor dl, dl ; x = 0
mov ax, msg_load_complete ; адрес строки сообщения об успешной загрузке
call PRINT_STRING ; выводим строку
; зафиксируем сообщения перед переходом на загруженный сектор
inc dh ; Y + 1
xor dl, dl ; X = 0
mov ax, msg_press_key ; строка — нажмите клавишу
call PRINT_STRING
call PRESS_KEY ; функция ожидания нажатия клавиши
jmp 600h ; переходим на адрес загруженного сектора (в пределах сегмента, ближний переход
; выводим об ошибке загрузки сектора, al — код ошибки
.ERROR:
inc dh ; Y + 1
xor dl, dl ; X = 0
push ax ; сохраняем код ошибки
mov ax, msg_err_load ; строка сообщения об ошибки
call PRINT_STRING
pop ax ; восстанавливаем код ошибки
call PRINT_HEX_BYTE ; вывод байта кода ошибки
jmp $ ; зацикливаемся
; вывод строки на экран
; ax — адрес выводимой строки
; dl — колонка (X) курсора
; dh — строка (Y) курсора
PRINT_STRING:
push ax
push si
pushf ; сохраняем регистр флагов
mov si, ax
cld ; направление для строковых данных, машинный код FC. Флаг DF = 0. Указатель SI = +1
.loops:
lodsb ; считать байт по адресу DS:SI в AL, машинный код AC
test al, al ; проверяем конец строки
jz .return
call PRINT_CHAR ; выводим символ
inc dl ; X = X + 1
jmp .loops
.return:
popf
pop si
pop ax
ret ; возврат из процедуры. Машинный код C3 — близкий возврат, в пределах сегмента. Машинный код CB — дальний возврат
; вывод на экран слова (16-бит)
; ax — выводимое слово
; dl — колонка (X) курсора
; dh — строка (Y) курсора
PRINT_HEX_WORD:
push ax
mov al, ah
call PRINT_HEX_BYTE ; выводим старший байт
pop ax
inc dl
call PRINT_HEX_BYTE ; выводим младший байт
ret
; вывод на экран байта (8-бит)
; al — выводимый байт
; dl — колонка (X) курсора
; dh — строка (Y) курсора
PRINT_HEX_BYTE:
push ax
push cx
mov ah, al ; сохраняем байт, для вывода младших 4-бит
mov cx, 4 ; для сдвига на 4 бита
shr al, cl ; сдвигаем старшие 4-бита в право
call PRINT_HEX_DIGIT ; выводим на экран старшие 4-бита
inc dl ; X = X + 1 (смещаем курсор в право)
mov al, ah ; возвращаем байт для обработки младших 4-бит
and al, 0Fh ; сбрасываем старшие 4-бита, чтобы остались только младшие 4-бита
call PRINT_HEX_DIGIT ; выводим на экран младшие 4-бита
pop cx
pop ax
ret
; перевод 4 бита в шестьнадцатеричный символ
; al — выводимый байт
; dl — колонка (X) курсора
; dh — строка (Y) курсора
PRINT_HEX_DIGIT:
push ax
cmp al, 0Ah ; число больше или равно 10 ?
jae .HEX_LETTER ; jae — переход если больше или равно, если флаг CF = 0 то переход
add al, 30h ; складываем al с кодом ASCII символа нуль (код 48(дес)), тем самым получая символы 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
jmp .WRITE_DIGIT
.HEX_LETTER:
sub al, 0Ah ; вычитаем 10 из преобразуемого числа
add al, 41h ; результат складываем с кодом ASCII символа(A = 65(дес)) + (0 … 5), тем самым получая символы A, B, C, D, E, F
.WRITE_DIGIT:
call PRINT_CHAR ; вывод символа на экран
pop ax
ret
; вывод символа на экран
; al — код символа
; dl — колонка (X) курсора
; dh — строка (Y) курсора
PRINT_CHAR:
push ax
push bx
push cx
push si
push di
push bp
push ax
xor bh, bh ; страница видеопамяти = 0
mov ah, 02h ; код функции установка курсора
int 10h
pop ax
mov ah, 09h ; код функции — писать символ/атрибут в текущей позиции курсора
mov cx, 1 ; счетчик (сколько экземпляров символа записать)
mov bl, 8 ; видео атрибут (текст) или цвет (графика)
int 10h ; вызов функции
pop bp
pop di
pop si
pop cx
pop bx
pop ax
ret
PRESS_KEY:
push ax
mov ah, 00h ; читать (ожидать) следующую нажатую клавишу. AL=ASCII символ
int 16h
pop ax
ret
data_text:
msg_start db «Bootloader start v2.0»,0 ; db — define byte — определить байт
msg_segment db «Segment start #», 0
msg_press_key db «Press key…», 0
msg_load_complete db «Loading sector #2», 0
msg_err_load db «Loading failed. Error code #», 0
msg_disk_start db «Disk start #», 0
msg_adress db «Adress start #», 0
data_v:
disk_start db 0 ; здесь диск с которого стартовали
size: dw $ — start ; здесь размер кода с данными
finish:
times 0x1fe — finish + start db 0 ; заполняем нулями оставшееся адресное пространство
signatura:
db 55h, 0xAA
Надо тебе прям отдельную рублику заводить 🙂 Когда ожидать выход ZX OS что бы заменить виду ? 🙂
На счёт ОС я не знаю, а вот рубрику создай, я тебе ещё напишу ))