Пример использования интерфейса BIOS
Наш пример демонстрирует использование функции 89h прерывания INT 15h для установки защищённого режима работы процессора. Программа устанавливает защищённый режим, выдаёт первое сообщение и через некоторое время, выдав второе сообещние, возвращается в реальный режим. После того, как будет нажата любая клавиша, работа программы будет завершена.
Обратите внимание на то, как в файле tos.c подготавливается таблица GDT. Адрес подготовленной таблицы передаётся функции protected_mode(), которая передаёт его функции 89h прерывания INT 15h. Вызов этой функции выполняется в файле tossyst.asm.
Листинг 16. Определение констант и структур данных
Файл tos.h -----------------------------------------------------------
#define word unsigned int
// Селекторы, определённые в GDT
#define GDT_SELECTOR 0x08 // 1 - селктор для GDT #define IDT_SELECTOR 0x10 // 2 - селектор для IDT #define DATA_SELECTOR 0x18 // 3 - селектор для DS
#define VID_MEM_SELECTOR 0x20 // 4 - селектор для ES, // будет использован для адресации видеопамяти
#define SS_SELECTOR 0x28 // 5 - селектор для SS #define CODE_SELECTOR 0x30 // 6 - селектор для CS #define BIOS_SELECTOR 0x38 // 7 - селектор для адресации // области данных BIOS
#define COLOR_VID_MEM 0xb8000L #define MONO_VID_MEM 0xb0000L #define MONO_MODE 0x07 #define BW_80_MODE 0x02 #define COLOR_80_MODE 0x03
typedef struct descriptor { word limit; word base_lo; unsigned char base_hi; unsigned char type_dpl; unsigned reserved; } descriptor;
typedef struct gate { word offset; word selector; unsigned char count; unsigned char type_dpl; word reserved; } gate;
#define DESCRIPTOR_SIZE (sizeof(descriptor)) #define GATE_SIZE (sizeof(gate)) #define IDT_SIZE (sizeof(idt))
#define TYPE_CODE_DESCR 0x18 #define TYPE_DATA_DESCR 0x10 #define TYPE_INTERRUPT_GATE 0x86 #define TYPE_TRAP_GATE 0x87 #define SEG_WRITABLE 0x02 #define SEG_READABLE 0x02 #define SEG_EXPAND_DOWN 0x04 #define SEG_ACCESSED 0x01 #define SEG_PRESENT_BIT 0x80
#define EOI 0x20 #define MASTER8259A 0x20 #define SLAVE8259A 0xA0
#define MK_LIN_ADDR(seg,off) (((unsigned long)(seg))<<4)+(word)(off)
Листинг 17.
Главная программа
Файл tos.c -----------------------------------------------------------
#include <stdio.h> #include <stdlib.h> #include <dos.h> #include <conio.h> #include "tos.h"
void Init_And_Protected_Mode_Entry(void); void protected_mode(descriptor far *gdt_ptr); void real_mode(void); void init_gdt_descriptor(descriptor *descr, unsigned long base, word limit, unsigned char type); void vi_print(unsigned int x, unsigned int y, char *s, char attr); void vi_hello_msg(void);
void exception_0(void); //{ prg_abort(0); } void exception_1(void); //{ prg_abort(1); } void exception_2(void); //{ prg_abort(2); } void exception_3(void); //{ prg_abort(3); } void exception_4(void); //{ prg_abort(4); } void exception_5(void); //{ prg_abort(5); } void exception_6(void); //{ prg_abort(6); } void exception_7(void); //{ prg_abort(7); } void exception_8(void); //{ prg_abort(8); } void exception_9(void); //{ prg_abort(9); } void exception_A(void); //{ prg_abort(0xA); } void exception_B(void); //{ prg_abort(0xB); } void exception_C(void); //{ prg_abort(0xC); } void exception_D(void); //{ prg_abort(0xD); } void exception_E(void); //{ prg_abort(0xE); } void exception_F(void); //{ prg_abort(0xF); } void exception_10(void); //{ prg_abort(0x10); } void exception_11(void); //{ prg_abort(0x11); } void exception_12(void); //{ prg_abort(0x12); } void exception_13(void); //{ prg_abort(0x13); } void exception_14(void); //{ prg_abort(0x14); } void exception_15(void); //{ prg_abort(0x15); } void exception_16(void); //{ prg_abort(0x16); } void exception_17(void); //{ prg_abort(0x17); } void exception_18(void); //{ prg_abort(0x18); } void exception_19(void); //{ prg_abort(0x19); } void exception_1A(void); //{ prg_abort(0x1A); } void exception_1B(void); //{ prg_abort(0x1B); } void exception_1C(void); //{ prg_abort(0x1C); } void exception_1D(void); //{ prg_abort(0x1D); } void exception_1E(void); //{ prg_abort(0x1E); } void exception_1F(void); //{ prg_abort(0x1F); }
void iret0(void); void iret1(void);
descriptor gdt[8];
gate idt[] = { { (word)&exception_0, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 0 { (word)&exception_1, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 1 { (word)&exception_2, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 2 { (word)&exception_3, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 3 { (word)&exception_4, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 4 { (word)&exception_5, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 5 { (word)&exception_6, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 6 { (word)&exception_7, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 7 { (word)&exception_8, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 8 { (word)&exception_9, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 9 { (word)&exception_A, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // A { (word)&exception_B, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // B { (word)&exception_C, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // C { (word)&exception_D, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // D { (word)&exception_E, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // E { (word)&exception_F, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // F { (word)&exception_10, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 10 { (word)&exception_11, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 11 { (word)&exception_12, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 12 { (word)&exception_13, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 13 { (word)&exception_14, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 14 { (word)&exception_15, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 15 { (word)&exception_16, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 16 { (word)&exception_17, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 17 { (word)&exception_18, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 18 { (word)&exception_19, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 19 { (word)&exception_1A, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 1A { (word)&exception_1B, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 1B { (word)&exception_1C, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 1C { (word)&exception_1D, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 1D { (word)&exception_1E, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 1E { (word)&exception_1F, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 1F
{ (word)&iret0, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 20 { (word)&iret0, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 21 { (word)&iret0, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 22 { (word)&iret0, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 23 { (word)&iret0, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 24 { (word)&iret0, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 25 { (word)&iret0, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 26 { (word)&iret0, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 27
{ (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 28 { (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 29 { (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 2A { (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 2B { (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 2C { (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 2D { (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 2E { (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 } // 2F };
word y=0;
void main(void) {
textcolor(BLACK); textbackground(LIGHTGRAY); clrscr(); Init_And_Protected_Mode_Entry(); enable_interrupt();
vi_hello_msg();
y=3; vi_print(0, y++, " Вошли в защищённый режим", 0x7f); pause(); vi_print(0, y++, " Для возврата в реальный режим нажмите любую клавишу", 0x7f);
real_mode(); getch(); textcolor(WHITE); textbackground(BLACK); clrscr(); }
void init_gdt_descriptor(descriptor *descr, unsigned long base, word limit, unsigned char type) {
descr->base_lo = (word)base; descr->base_hi = (unsigned char)(base >> 16); descr->type_dpl = type; descr->limit = limit; descr->reserved = 0; }
void Init_And_Protected_Mode_Entry(void) {
union REGS r; word crt_mode; extern word gv1_;
// Дескриптор, описывающий таблицу GDT
init_gdt_descriptor(&gdt[1], MK_LIN_ADDR(_DS, &gdt), sizeof(gdt)-1, TYPE_DATA_DESCR | SEG_PRESENT_BIT | SEG_WRITABLE);
// Дескриптор, описывающий таблицу IDT
init_gdt_descriptor(&gdt[2], MK_LIN_ADDR(_DS, &idt), (unsigned long)IDT_SIZE-1, TYPE_DATA_DESCR | SEG_PRESENT_BIT | SEG_WRITABLE);
// Дескриптор сегмента данных
init_gdt_descriptor(&gdt[3], MK_LIN_ADDR(_DS, 0), 0xffffL, TYPE_DATA_DESCR | SEG_PRESENT_BIT | SEG_WRITABLE);
// Определяем текущий видеорежим
r.h.ah=15; int86(0x10,&r,&r); crt_mode = r.h.al;
// Инициализация дескриптора для видеопамяти // монохромного видеоадаптера
if(crt_mode == MONO_MODE) init_gdt_descriptor(&gdt[4], MONO_VID_MEM, 3999, TYPE_DATA_DESCR | SEG_PRESENT_BIT | SEG_WRITABLE);
// Инициализация дескриптора для видеопамяти // цветного видеоадаптера
else if(crt_mode == BW_80_MODE || crt_mode == COLOR_80_MODE) init_gdt_descriptor(&gdt[4], COLOR_VID_MEM, 3999, TYPE_DATA_DESCR | SEG_PRESENT_BIT | SEG_WRITABLE); else { printf("\nИзвините, этот видеорежим недопустим."); exit(-1); }
// Дескриптор для сегмента стека
init_gdt_descriptor(&gdt[5], MK_LIN_ADDR(_DS, 0), 0xffffL, TYPE_DATA_DESCR | SEG_PRESENT_BIT | SEG_WRITABLE);
// Дескриптор для сегмента кода
init_gdt_descriptor(&gdt[6], MK_LIN_ADDR(_CS, 0), 0xffffL, TYPE_CODE_DESCR | SEG_PRESENT_BIT | SEG_READABLE);
// Входим в защищённый режим // В качестве параметра передаём адрес подготовленной // таблицы GDT
protected_mode(gdt); }
void prg_abort(int err);
void exception_0(void) { prg_abort(0); } void exception_1(void) { prg_abort(1); } void exception_2(void) { prg_abort(2); } void exception_3(void) { prg_abort(3); } void exception_4(void) { prg_abort(4); } void exception_5(void) { prg_abort(5); } void exception_6(void) { prg_abort(6); } void exception_7(void) { prg_abort(7); } void exception_8(void) { prg_abort(8); } void exception_9(void) { prg_abort(9); } void exception_A(void) { prg_abort(0xA); } void exception_B(void) { prg_abort(0xB); } void exception_C(void) { prg_abort(0xC); } void exception_D(void) { prg_abort(0xD); } void exception_E(void) { prg_abort(0xE); } void exception_F(void) { prg_abort(0xF); } void exception_10(void) { prg_abort(0x10); } void exception_11(void) { prg_abort(0x11); } void exception_12(void) { prg_abort(0x12); } void exception_13(void) { prg_abort(0x13); } void exception_14(void) { prg_abort(0x14); } void exception_15(void) { prg_abort(0x15); } void exception_16(void) { prg_abort(0x16); } void exception_17(void) { prg_abort(0x17); } void exception_18(void) { prg_abort(0x18); } void exception_19(void) { prg_abort(0x19); } void exception_1A(void) { prg_abort(0x1A); } void exception_1B(void) { prg_abort(0x1B); } void exception_1C(void) { prg_abort(0x1C); } void exception_1D(void) { prg_abort(0x1D); } void exception_1E(void) { prg_abort(0x1E); } void exception_1F(void) { prg_abort(0x1F); }
void prg_abort(int err) {
vi_print(1,y++,"---> Произошло исключение", 0xc); real_mode(); gotoxy(1,24); cprintf("Исключение %X, нажмите любую клавишу", err); getch();
textcolor(WHITE); textbackground(BLACK); clrscr(); exit(0);
}
void iret0(void) { asm { push ax mov al,EOI out MASTER8259A,al pop ax pop bp iret } }
void iret1(void) { asm { push ax mov al,EOI out MASTER8259A,al out SLAVE8259A,al pop ax pop bp iret } }
void vi_putch(unsigned int x, unsigned int y ,char c, char attr) {
register unsigned int offset; char far *vid_ptr;
offset=(y*160) + (x*2); vid_ptr=MK_FP(VID_MEM_SELECTOR, offset); *vid_ptr++=c; *vid_ptr=attr; }
void vi_print(unsigned int x, unsigned int y, char *s, char attr) { while(*s) vi_putch(x++, y, *s++, attr); }
void vi_hello_msg(void) {
vi_print(0, 0, " Protected mode monitor *TINY/OS*, " "v.1.11 for CPU 80286 ¦ © Frolov A.V., 1992 ", 0x30);
}
Листинг 18.
Функции для перехода в защищённый режим и возврата в реальный режим.
Файл tossyst.asm -----------------------------------------------------------
IDEAL MODEL SMALL RADIX 16 P286
DATASEG
CMOS_PORT EQU 70 PORT_6845 EQU 63h COLOR_PORT EQU 03d4h MONO_PORT EQU 03b4h STATUS_PORT EQU 64h SHUT_DOWN EQU 0feh INT_MASK_PORT EQU 21h VIRTUAL_MODE EQU 0001 A20_PORT EQU 0d1 A20_ON EQU 0df A20_OFF EQU 0ddh EOI EQU 20 MASTER8259A EQU 20 SLAVE8259A EQU 0a0h KBD_PORT_A EQU 60h KBD_PORT_B EQU 61h
gdt_off dw ? gdt_seg dw ?
real_ss dw ? real_sp dw ? real_es dw ?
CODESEG
PUBLIC _real_mode, _protected_mode PUBLIC _enable_interrupt PUBLIC _pause
PROC _protected_mode NEAR push bp mov bp,sp
mov ax,[bp+4] mov dx,[bp+6] mov [gdt_seg], dx mov [gdt_off], ax
push ds mov ax,40 mov ds,ax mov [WORD 67],OFFSET shutdown_return mov [WORD 69],cs pop ds
cli in al, INT_MASK_PORT and al, 0ffh out INT_MASK_PORT, al
mov al,8f out CMOS_PORT,al jmp delay1 delay1: mov al,5 out CMOS_PORT+1,al
mov [real_ss],ss mov [real_es],es
; Загружаем регистры ES:SI адресом GDT, полученным ; как параметр функции protected_mode()
mov es, [gdt_seg] mov si, [gdt_off]
; Подготавливаем номера прерываний IRQ0 и IRQ8 ; для перепрограммирования контроллеров прерываний.
mov bx, 2028h
; Устанавливаем защищённый режим работы
mov ax, 8900h int 15h jnc pok
; Если произошла ошибка, мы остались в реальном режиме, ; завершаем работу программы.
mov ah, 4ch int 21h
; Установлен защищённый режим работы процессора !
pok: pop bp ret ENDP _protected_mode
PROC _real_mode NEAR
mov [real_sp], sp mov al, SHUT_DOWN out STATUS_PORT, al waitr1: hlt jmp waitr1
LABEL shutdown_return FAR
mov ax, DGROUP mov ds, ax
assume ds:DGROUP
cli mov ss,[real_ss] mov sp,[real_sp]
in al, INT_MASK_PORT and al, 0 out INT_MASK_PORT, al
mov ax, DGROUP mov ds, ax mov ss, ax mov es, ax
mov ax,000dh out CMOS_PORT,al sti ret ENDP _real_mode
PROC _pause NEAR
push cx mov cx,10 ploop0: push cx xor cx,cx ploop1: loop ploop1 pop cx loop ploop0
pop cx ret
ENDP _pause
PROC _enable_interrupt NEAR
sti in al, INT_MASK_PORT and al, 0fch out INT_MASK_PORT, al
ret ENDP _enable_interrupt
end