Завантажувальний CD своїми руками №2

@keypress: xor ax,ax; читаємо клавішу int 16h

test al, al; перевіримо, розширений ASCII чи ні jz @extended_scancode

@extended_scancode: cmp ah, 48h; стрілка вгору jz MoveCursorUp

cmp ah, 50h; стрілка вниз jz MoveCursorDown jmp @keypress ret

При натисканні кнопок вгору і вниз викликаються відповідні підпрограми переміщення курсору MoveCursorUp і MoveCursorDown.

; Процедура виведення на екран одного символу ; dh, dl - рядок, стовпець; bl - колір; al - символ

PrintSymbol: pusha xor bh,bh mov ah,02h; встановимо курсор int 10h mov ah,09h; і виведемо символ al на екран mov cx,1 int 10h popa ret

Ми повинні організувати переміщення курсору вгору та вниз, при цьому необхідно обмежити рух курсору, щоб він не виходив за межі меню. Щоб визначати положення курсору, створимо змінну MenuItemSelected, яка змінюватиметься щоразу, як змінюється положення курсору та показуватиме номер вибраного пункту меню (починаючи з нуля). Для цього нам знадобляться ще дві змінні (в принципі їх можна було б оголосити константами) - MenuFirstItemY - позиція першого пункту меню і MenuLastItemY - позиція останнього пункту меню по осі Y. Звичайно зручніше було б вказати положення першого пункту та кількість елементів меню, але це спричинить збільшення коду, та й щоразу перераховувати MenuLastItemY не розумно. Якщо координата курсора дорівнюватиме одному з крайніх положень, то перемальовувати його немає сенсу. Не слід забувати, що нумерація рядків і стовпців починається з нуля, а не з одиниці.

PositionXY dw 0000h; у цій змінній будемо зберігати координати курсору MenuItemSelected db 00h MenuFirstItemY db 00h MenuLastItemY db 00h

MoveCursorUp: pusha mov dx,[PositionXY] cmp dh,[MenuFirstItemY] ; перевіримо, чи знаходиться курсор у крайньому положенні jz @1; якщо так, то стрибок наприкінці процедури mov bl,2 ; колір - зелений mov al, '; зітремо старий курсор, написавши на його місці пропуск call PrintSymbol dec dh; зменшимо координату Y курсору, тим самим піднявши його нагору mov al,'>' ; al - символ курсору call PrintSymbol mov [PositionXY], dx; збережемо нові координати курсора sub dh, [MenuFirstItemY] ; порахуємо номер вибраного пункту меню mov [MenuItemSelected],dh @1: popa jmp @keypress

; У цій процедурі все те саме, тільки координата Y курсору збільшується на одиницю

MoveCursorDown: pusha mov dx,[PositionXY] cmp dh,[MenuLastItemY] jz @2 mov bl,2 mov al,' ' call PrintSymbol inc dh; збільшимо координату Y курсору, тим самим опустивши його вниз mov al,'>' call PrintSymbol mov [PositionXY],dx sub dh,[MenuFirstItemY] mov [MenuItemSelected] ,dh @2: popa jmp @keypress

Ну що ж, тепер ми вже маємо що подивитися. Для цього нам і знадобиться емулятор Bochs. Якщо його знайти не вдалося, можна скористатися програмою RawwriteWin (http://uranus.it.swin.edu.au/

jn/linux) або аналогічною, яка може записувати образ на дискету. Такі програми легко знайти в інтернеті. Створимо файл main.asm.

Main_Program: call DrawInterface

mov ax,0303h; задаємо початкове положення курсору (X=3, Y=3) mov [PositionXY],ax

mov [MenuFirstItemY],2; задаємо область, в якій може рухатися курсор mov [MenuLastItemY],6

call MoveCursorUp; щоб намалювати курсор, помістимо його на одиницю нижче

call KeybInput ret

Бувайтут відбувається виклик процедур, що відповідають за оформлення та клавіатуру та ініціалізація змінних, що відповідають за курсор меню. Не забудемо підключити відповідні файли:

include ‘graphic.inc’ include ‘keyb.inc’

Створимо файл make.bat. Кючеве слово include фактично поєднує два файли в один, тому достатньо написати файл, з якого все починається:

fasm bootsect.asm image.bin

Запустивши цей .bat ми отримаємо готовий файл образу image.bin. Непогано було б зробити, щоб вікно не закривалося відразу, тому що якщо не створився кінцевий файл, виведеться помилка, тому в кінці bat'ника зручно написати

Для початку емуляції Bochs необхідно налаштувати - задати параметри машини, що емулюється. Для цього є конфігураційний файл. Створимо файл adjustments наступного змісту (це для версії 2.1, але практично те саме і в інших версіях, докладну інформацію можна знайти в документації Bochs http://bochs.sf.net/docs-html та http://bochs.sf.net/doc/docbook/index.html):

floppya: 1_44= диск\…\…\image.bin, status=inserted floppyb: 1_44= a:, status=ejected romimage: file=BIOS-bochs-latest, address=0xf0000 vgaromimage : VGABIOS-elpin-2.40 megs: 32 boot: floppy vga_update_interval: 30000 keyboard_serial_delay: 250 floppy_command_delay: 5000 ips: 50 private_colormap: enabled=0 i440fxsupport: enabled=0 time0: 0 newharddrivesupport: enabled=1 log: — panic: action=ask error: action = report info: action = report debug: action = ignore

Цей файл повинен знаходитись у папці, де знаходиться Bochs. Після останнього рядка обов'язково повинен стояти перехід на наступний рядок, в іншому випадку Bochs починає кричати і не запускається. ДисководB: (реальний дисковод) можна увімкнути, натиснувши Bochs відповідний значок і поставивши галочку у inserted.

Далі створимо файл run.bat:

cd диск\папка, де встановлено Bochs\ bochs -q -f adjustments

У Bochs можна вантажиться і з реальної дискети, для цього треба написати (за місце першого рядка)

floppya: 1_44=A:, status=inserted

б) За відсутності Bochs або іншого емулятора

За відсутності емулятора доведеться робити все вручну. На дискету (зовсім необов'язково форматовану) за допомогою якоїсь програми (наприклад, Rawwrite або RawwriteWin) записується образ, після чого комп'ютер перезавантажується, а в BIOS встановлюється завантаження з дискети. Якщо ви використовуєте RawwriteWin, то можна створити .bat файл:

rawwritewin-write-copies 1-drive 0 image.bin

В даному випадку run.bat не потрібний.

На справжній машині, звичайно, надійніше, оскільки емулятор це все-таки емулятор. Я кілька разів стикався з ситуацією, коли в емуляторі програма зовсім відмовлялася нормально працювати, хоча чудово працювала на реальній машині (це не стосується програми, яку ми зараз пишемо, вона працювала і там, і там). Зате на реальній машині немає таких можливостей дебаггінгу (якщо на реальній машині і без операційної системи вони взагалі є ), та при використанні емулятора комп'ютер не треба постійно перезавантажувати.

Запускаємо run.bat і насолоджуємось результатом 😉 . Правда, за відсутності емулятора насолода буде дещо зіпсована необхідністю перезавантаження комп'ютера.

Курсор рухається, але поки нічого вибрати не можна, тому файл keyb.bat додамо обробку натискання клавіші Enter. Додамо перевірку на enter в нескінченний цикл:

cmp al, 0dh; enter jz@key_enter

У цьому обробнику ми дивитимемося, що треба робити залежно від номера пункту меню.

; обробка натискання Enter @key_enter: cmp [MenuItemSelected],0 jz BootFromCD

cmp [MenuItemSelected],1 jz BootFromFloppy

cmp [MenuItemSelected],2 jz BootFromHDA1

cmp [MenuItemSelected],3 jz BootFromHDB1

cmp [MenuItemSelected],4 jz @MenuItem_Int19h jmp @keypress

;Quick reboot (using int19h) @MenuItem_Int19h: int 19h ; «гаряче» перезавантаження jmp @keypress

Для "гарячого" перезавантаження (при якому BIOS заново перевіряє все, з чого можна завантажиться не перезавантажуючи комп'ютер повністю) потрібно викликати переривання int 19h без будь-яких параметрів.

Тепер створимо файл boot.inc, де опишемо відповідні процедури. Загальний код читання для всіх дисків один (іде після мітки @boot_system), так як бутсектор завжди знаходиться в одному місці: головка 0, циліндр 0, сектор 1. Після завантаження з CD-ROM дисковод стане диском B, образ при емуляції буде диском A. Отже, пристрої: dl = 01h – диск B, dl = 80h – Primary Master, dl = 81h – Primary Slave. Після спроби читання проходить перевірка на помилки. Якщо їх немає, то перевіримо наявність сигнатури 55AAh, і за її наявності передамо керування завантаженим бутсектором.

BootFromFloppy: mov dl,01h jmp @boot_system

BootFromHDA1: mov dl,80h jmp @boot_system

BootFromHDB1: mov dl,81h

@boot_system: push 0000 pop es mov bx,7c00h

mov ah,02h mov cl,01h mov al,01h mov dh,00h mov ch,00h int 13h jc @read_error ; перевіримо, чи не було помилок читання

mov bx,7DFEh mov ax,[bx] cmp ax,55AAh jz @invalid_signature

У разі будь-якої помилки на екран будутьвиводитися лайливі написи. Для цього напишемо допоміжну процедуру виведення рядка, а для того, щоб щоразу не вказувати в dx координати курсору, bl колір і т.д., напишемо допоміжну процедуру. Ця процедура буде виводити на екран рядок наступного формату: X, Y, колір, текст,

PrintStrF: pusha mov dx,[si] add si,2 mov bl,[si] inc si @put_symb: lodsb cmp al,0 jz @end_of_string call PrintSymbol inc dl jmp @put_symb @end_of_string: popa ret

Створимо файл text.inc, в якому зберігатимемо всі написи, і не забудемо підключити його:

Щоб не виникло проблем, що на екрані залишилася частина старого напису, наведемо всі написи до однакової довжини пробілами.

FloppyDriveReadError db 5,9,4, 'Error reading Floppy Drive B: ',0 HDA1ReadEror db 5,9,4, 'Error reading Prim. 'Error reading Primary Slave Hard Disk ',0 InvalidSignature db 5,9,4, 'Invalid Boot Signature. Continue anyway (y/n)? ‘,0

Виведемо різні повідомлення, залежно від того, яка помилка сталася (boot.inc):

@read_error: lea si,[FloppyDriveReadError] cmp dl,01h jz @PrintError

lea si,[HDA1ReadEror] cmp dl,80h jz @PrintError

@PrintError: call PrintStrF jmp @keypress

@invalid_signature: lea si,[InvalidSignature] call PrintStrF xor ax,ax; прочитаємо клавішу int 16h cmp al,'Y'; якщо Y, то продовжуємо jz @boot_anyway jmp @keypress

Тепер починається найцікавіше - завантаження з дискети, на якій записана наша програма. Адже нормального бутсектора там, де він має бути, — ні. Якщо у когось виникне бажання, тут ви можете самі написати завантажувач для DOS'a. А ми вчинимо простіше.скористаємося готовим бутсектором. Щоб його отримати, створимо нормальну завантажувальну дискету, запустивши досівський формат з параметром \s:

На жаль, це без проблем працює тільки для користувачів Windows 98 і DOS'а, а цим навряд чи хтось зараз користується. Користувачі NT систем зіткнуться з проблемою - а де взяти дос. Ну, напевно у вас знайдеться якийсь завантажувальний компакт-диск, а вже там (якщо це не ліцензійна вінда або Linux) є. Витягти його звідти непросто - він знаходиться у вигляді образу або захований десь на початку диска і не видно як файл, та й не потрібно. Потрібно просто завантажиться з цього диска і відформатувати дискету як описано вище.

Тепер бутсектор потрібно витягти. Для цього можна скористатися Norton Disk Editoro або будь-якою іншою аналогічною програмою. Програму для отримання бутсектора можна написати і самому. Просто прочитаємо перший сектор дискети (як це робили вище) і за допомогою функцій DOS збережемо його. Використання функцій ДОС зовсім не означає непрацездатності її під Windows — вона працює під будь-якою версією.