;06V86.asm ; Program prepne MS-DOS jako V86 proces v chranenem rezimu 80386. ; Program lze spustit pouze v realnem rezimu pod operacnim systemem ; MS-DOS. V systemu nesmi byt zavedeny zadne ovladace pracujici ; s dodatecnou pameti (RAMDRIVE.SYS, HIMEM.SYS, ...). .386p ; Program bude pouzivat instrukcni repertoar procesoru 80386. Skupina GROUP DATA,STKSEG,CODE DATA SEGMENT PUBLIC USE16 'DATA' ; Segment podle 16bitovych pravidel. desc STRUC ; Definice polozky tabulky popisovacu segmentu Limit dw 0 ; GDT,LDT: limit segmentu (15-0) IDT: offset (15-0) Base_l dw 0 ; baze segmentu (15-0) selektor Base_m db 0 ; baze segmentu (23-16) 0 Access db 0 ; pristupova prava pristupova prava Gran db 0 ; G, s, 0, AVL, limit (19-16) offset (23-16) Base_h db 0 ; baze segmentu (31-24) offset (31-24) desc ENDS ; Kazda polozka tabulky popisovacu ma velikost 8 slabik. ; Definice GDT: DefGDT desc <> ; Prvni polozka GDT je "neplatny selektor" (nepouziva se) ; Limit DPL Typ DefLDT3 desc < 7,0,0,11100010b, 0,0> ; 7 3 syst.segment s LDT DefLDT0 desc < 7,0,0,10000010b, 0,0> ; 7 0 syst.segment s LDT DefDtS3 desc <0FFFFh,0,0,10010010b,0Fh,0> ; FFFFFh 0 datovy, ED=0, W=1 DefIntT desc <0FFFFh,0,0,10010010b,0Fh,0> ; FFFFFh 0 datovy, ED=0, W=1 DefDtS0 desc <0FFFFh,0,0,10010010b,0Fh,0> ; FFFFFh 0 datovy, ED=0, W=1 DefStrt desc ; vel.TSS 0 TSS neaktiv.proc. DefV86 desc ; vet.TSS 0 TSS aktivniho pr. DefSupC desc <0FFFFh,0,0,10011010b,0Fh,0> ; FFFFFh 0 instrukcni,C=0,R=1 DefSupD desc <0FFFFh,0,0,10010010b,0Fh,0> ; FFFFFh 0 datovy, ED=0, W=1 DefSupS desc < 0,0,0,10010110b, 0,0> ; 0 0 zasobnik, ED=1,W=1 SzGDT equ $-DefGDT ; Konec GDT. SzGDT je velikost GDT ve slabikach. ; Hodnoty selektoru segmentu: SelLDT3 equ DefLDT3-DefGDT ; LDT procesu V86 (na urovni 3). SelLDT0 equ DefLDT0-DefGDT ; LDT startovaciho procesu (na urovni 0). SelDtS3 equ DefDtS3-DefGDT ; Datovy segment prekryvajici zasobnik ; V86 procesu. SelIntT equ DefIntT-DefGDT ; Selektor segmentu obsahujiciho tabulku ; prerusovacich vektoru V86 (baze=0). SelDtS0 equ DefDtS0-DefGDT ; Datovy segment prekryvajici zasobniky. SelStrt equ DefStrt-DefGDT ; TSS procesu, ktery po IRET startuje V86. SelV86 equ DefV86-DefGDT ; TSS virtualniho 8086 procesu. SelSupC equ DefSupC-DefGDT ; Instrukcni segment ridiciho procesu. SelSupD equ DefSupD-DefGDT ; Datovy segment ridiciho procesu. SelSupS equ DefSupS-DefGDT ; Zasobnikovy segment ridiciho procesu. ; .SALL ; Definice IDT (.SALL je zakaz rozepisovani generovanych maker) DefIDT equ $ ; Zacatek IDT REPT 256 ; Makro opakuj 256krat, tj. generuj 256 popisovacu: desc <0,SelSupC,0,0EEh,0,0> ; Offset=0, Selektor=0, DPL=3, Typ= ENDM ; brana pro maskujici preruseni 80386 SzIDT equ $-DefIDT ; Konec IDT. SzIDT je velikost IDT ve slabikach ; ; Definice 32bitove struktury TSS procesu 80386. StrukTSS STRUC ; OFFSET tBack dw 0 ; 0 Zpetny ukazatel dw 0 ; 2 - tESP0 dd 400h ; 4 ESP pro uroven 0 tSS0 dw SelSupS ; 8 SS pro uroven 0 dw 0 ; 10 - tESP1 dd 400h ; 12 ESP pro uroven 1 tSS1 dw SelSupS ; 16 SS pro uroven 1 dw 0 ; 18 - tESP2 dd 400h ; 20 ESP pro uroven 2 tSS2 dw SelSupS ; 24 SS pro uroven 2 dw 0 ; 26 - tCR3 dd 0 ; 28 CR3 tEIP dd 0 ; 32 EIP tEFLAGS dd 2 ; 36 EFLAGS tEAX dd 0 ; 40 EAX tECX dd 0 ; 44 ECX tEDX dd 0 ; 48 EDX tEBX dd 0 ; 52 EBX tESP dd 1000h ; 56 ESP tEBP dd 0 ; 60 EBP tESI dd 0 ; 64 ESI tEDI dd 0 ; 68 EDI tES dw SelSupD ; 72 ES dw 0 ; 74 - tCS dw SelSupC ; 76 CS dw 0 ; 78 - tSS dw SelSupS ; 80 SS dw 0 ; 82 - tDS dw SelSupD ; 84 DS dw 0 ; 86 - tFS dw 0 ; 88 FS dw 0 ; 90 - tGS dw 0 ; 92 GS dw 0 ; 94 - tLDT dw SelLDT0 ; 96 Selektor LDT dw 0 ; 98 - dw 0 ; 100 T IOmap dw 104 ; 102 Offset mapy pristupnych V/V bran StrukTSS ENDS ; ; Definice TSS startovaciho procesu v chranenem rezimu. TSSstrt StrukTSS ; TSS pro odstartovani V86 (CPL:=3). SzTS equ $-TSSstrt ; Limit TSSstrt pro popisovac v GDT. ; Definice TSS procesu V86 v chranenem rezimu. TSSv86 StrukTSS <> ; TSS V86 OffIOmp equ $-TSSv86 ; Offset mapy V/V bran pro TSSv86. dw 4896 dup(0) ; Mapa V/V bran TSSv86 SzTS86 equ $-TSSv86 ; Limit TSSv86 pro popisovac v GDT. dw -1 ; Mapa musi byt zakoncena slabikou 0FFh. ; ; Definice struktury zasobniku urovne 0 po preruseni procesu V86: StrukZas STRUC ; OFFSET StareIP dw 0 ; 0 EIP (sem ukazuje SS:ESP po preruseni) dw 0 StareCS dw 0 ; 4 CS dw 0 StareEF dw 0 ; 8 EFLAGS dw 0 StareSP dw 0 ; 12 ESP dw 0 StareSS dw 0 ; 16 SS dw 0 StareES dd 0 ; 20 ES StareDS dd 0 ; 24 DS StareFS dd 0 ; 28 FS StareGS dd 0 ; 32 GS StrukZas ENDS ; ; Promenne pro rezidentni (ale i instalacni) cast programu. dLimit dw 0 ; Pomocna prom. pro instrukce LGDT a LIDT. dBase dd 0 ; Tyto dve prom. musi byt (!!) vedle sebe! Dummy dw 0 ; Promenna Dummy slouzi jako bezedna studna. Navrat dw 0 dsaddr dd 0 Zprava db 'Prave byl zapnut rezim virtualni 8086.',10,13,'$' DATA ENDS ; Konec datoveho segmentu. ; STKSEG SEGMENT para STACK USE16 'STACK' ; Segment se zasobnikem. DB 2000h DUP(?) ; SP=2000h pro realny rezim. ; Hloubka zasobniku je 2KB. ; SP=1800h pro uroven opravneni 0. ; Zasobnik je podle ; SP=1600h pro uroven opravneni 1. ; 16bitovych pravidel. ; SP=1400h pro uroven opravneni 2. STKSEG ENDS ; Zde je dno zasobniku. ; CODE SEGMENT PUBLIC USE16 'CODE' ; Vlastni program v segmentu opet ASSUME CS:CODE,SS:STKSEG,DS:DATA ; podle 16bitovych pravidel. REZID PROC ; Zacatek rezidentni casti MS-DOSovskeho programu. ObsluhaPreruseniCislo0: ; Posloupnost 256 3slabikovych instrukci REPT 256 ; instrukci CALL SpolecnaObsluha... a call SpolecnaObsluhaPreruseni ; 1slabikove instrukce IRET iret ; pro jednoduche urceni cisla ENDM ; generovaneho preruseni. ; IOPL ve V86 je 3: instrukce typu INT n, IRET, CLI, STI, PUSHF a POPF ; nezpusobi preruseni INT 13. SpolecnaObsluhaPreruseni: ; Vstupni bod rutiny pro obsluhu vsech push AX ; preruseni. Schovej AX do zasobniku. mov AX,SelSupD ; Napln segmentovy registr DS selektorem mov DS,AX ; zasobnikoveho segmentu pomoci AX. pop AX ; Obnov puvodni obsah AX. pop Navrat ; Vyber ze zasobniku navratovou adresu ; ulozenou instrukci CALL SpolecnaObsluhaPreruseni . cmp SP,1800h-4-SIZE StrukZas ; Je v zasobniku chybove slovo? jne Neni ; Ne, neni. pop Dummy ; Ano, je. 32bitove chybove slovo ze pop Dummy ; zasobniku vybereme a zahodime. Neni: pusha ; Do zasobniku vloz vsechny registry. mov AX,SelDtS0 ; ES naplnime selektorem datoveho segm. mov ES,AX ; prekryvajiciho zasobnik. mov BX,SP ; Registr BX napln obsahem SP. add BX,16 ; BX ukazuje nad registry ulozene PUSHA. mov AX,ES:[BX].StareEF+2 ; Ze zas. vezmi vyssi slovo EFLAGS. and AL,2 ; Je VM=1 (preruseni prislo z V86)? jnz OK ; Ano, OK. HLT ; VM=0, preruseni neprislo z V86 a to je katastrofa. OK: mov SI,Navrat ; Vezmi navratovou adresu CALL Spolecna... sub SI,OFFSET ObsluhaPreruseniCislo0 ; Odecti bazi sekvence. and SI,0fffch ; SI zarovnej na hodnotu delitelnou 4. cmp SI,13*4 ; Je prave obsluhovane preruseni INT 13? jne Neni13 ; Neni to INT 13. HLT ; Katastrofa, je to preruseni od obecne chyby ochrany. Neni13: ; Pripravime zasobnik V86 procesu tak, aby IRET se spravne vratil. mov AX,ES:[BX].StareSS ; Do AX vloz SS procesu V86, ktere mov CL,AH ; je podle pravidel 8086 (tedy segmentova shl AX,4 ; cast adresy). Tu uprav jako bazi noveho shr CL,4 ; datoveho segmentu, ktery se pres mov DefDtS3.Base_l,AX ; zasobnik V86 procesu prekryje. mov DefDtS3.Base_m,CL ; V popisovaci nastav jeho bazi. mov CX,ES:[BX].StareSP ; SP procesu V86 snizime o 6, coz sub CX,6 ; zahrnuje 3 16bitova slova nove mov ES:[BX].StareSP,CX ; vlozena do zasobniku: F, CS, IP. mov BP,ES:[BX].StareIP ; Vybereme hodnoty 16bitove hodnoty mov DI,ES:[BX].StareCS ; IP, CS a F a nachystame je pro mov AX,ES:[BX].StareEF ; ulozeni do zasobniku V86 procesu. mov DX,SelDtS3 ; DS napln selektorem datoveho segmentu mov DS,DX ; prekryvajiciho zasobnik V86 procesu. xchg BX,CX ; Vymen ukazatele do zasobniku. mov [BX]+4,AX ; Uloz F do zas. V86 (adresa je DS:BX). mov [BX]+2,DI ; Uloz CS do zasobniku V86. mov [BX],BP ; Uloz IP do zasobniku V86. and AX,0FCFFh ; Nastavime IF:=0 a TF:=0. ; IOPL zustava stale 3. mov BX,SelIntT ; DS napln selektorem segmentu mov DS,BX ; s tabulkou prer. vektoru procesu V86. mov BX,SI ; BX := cislo preruseni x 4 mov BP,[BX] ; BP := offset prerusovaciho vektoru mov DI,[BX]+2 ; DI := segment prerusovaciho vektoru mov BX,CX ; BX := ukazatel do spolecneho zasobniku mov ES:[BX].StareEF,AX ; Uloz F s nastavenym IOPL=0. mov ES:[BX].StareIP,BP ; Uloz IP podle prer. vektoru. mov ES:[BX].StareCS,DI ; Uloz CS podle prer. vektoru. xor AX,AX ; AX := 0 mov DS,AX ; Nastav neplatny obsah segmentovym mov ES,AX ; registrum DS a ES. popa ; Obnov vsechny registry ze zasobniku. iretd ; 32bitovy navrat z preruseni s IOPL=3 bez prepnuti ; procesu, ktery zahaji obsluhu preruseni podle pravidel ; 8086 (podle tabulky prer. vektoru). IOPL=3 zajisti, aby ; se pri IRET v V86 procesu nespoustela tato rutina. VelRezid db ? ; Maximalni adresa rezidentni casti. Rezid ENDP ; Konec rezidentni casti programu. ;--------------------- Instalacni cast programu ------------------------ Instal PROC ; Vstupni bod programu v realnem rezimu. mov AX,DATA ; Segmentovou cast adresy datoveho mov DS,AX ; segmentu DATA ulozime do DS a mov ES,AX ; take do ES. cld ; Retezce se budou zpracovavat odzadu. cli ; Preruseni bude po celou dobu behu programu zakazano. ; Definice nekolika uzitecnych maker: SegAdr MACRO segr ; MACRO SegAdr vygeneruje xor EAX,EAX ; posloupnost instrukci, mov AX,&segr ; ktera provede vypocet: shl EAX,4 ; EAX := segr x 16, kde ENDM ; "segr" je segmentovy registr. SetTSSa MACRO reg,tss,seg ; MACRO SetTSSa vygeneruje lea E®&X,&tss ; posloupnost instrukci, add E®&X,EAX ; ktera v popisovaci segmentu mov seg&.Base_l,reg&X ; zadanem parametrem "seg" shr E®&X,8 ; naplni bazi linearni adresou mov seg&.Base_m,reg&H ; TSS vypoctenou podle vyrazu: ENDM ; (OFFSET tss) + EAX . SetBase MACRO segr,segm ; MACRO SetBase vygeneruje SegAdr segr ; posloupnost instrukci, mov segm&.Base_l,AX ; ktera v popisovaci segmentu shr EAX,8 ; "segm" nastavi bazi na mov segm&.Base_m,AH ; hodnotu (segr x 16), kde ENDM ; "segr" je segmentovy registr. ; Konec definice maker. SetBase DS,DefSupD ; DefSupD.base := DSx16 SetBase CS,DefSupC ; DefSupC.base := CSx16 SetBase SS,DefSupS ; DefSupS.base := SSx16 SetBase SS,DefDtS0 ; DefDtS0.base := SSx16 SegAdr DS ; EAX := DSx16 mov dsaddr,EAX ; dsaddr := DSx16 ; Plneni tabulky IDT offsety prerusovacich rutin. xor BX,BX ; BX := 0 (adresa polozky IDT). lea AX,ObsluhaPreruseniCislo0 ; AX := OFFSET ObsluhaPrer... mov CX,256 ; Pocet polozek tabulky IDT. PlnIDT: mov DefIDT.Limit[BX],AX ; Napln offset polozky IDT. add AX,4 ; Offset rutiny obsluhy prerus. add BX,8 ; Offset polozky IDT. loop PlnIDT ; Opakuj 256x. ; Nastaveni obsahu popisovacu segmentu v GDT. SegAdr DS ; EAX := DSx16 SetTSSa D,TSSstrt,DefStrt ; DefStrt.Base:=OFFSET TSSstrt+DSx16 SetTSSa D,TSSv86,DefV86 ; DefV86.Base:=OFFSET TSSv86+DSx16 ; LDT obsahuji pouze neplatne selektory, a proto mohou ukazovat na GDT. SetTSSa D,DefGDT,DefLDT3 ; DefLDT3.Base:=OFFSET DefGDT+DSx16 SetTSSa D,DefGDT,DefLDT0 ; DefLDT0.Base:=OFFSET DefGDT+DSx16 ; Nastaveni obsahu TSS startovaciho procesu v chranenem rezimu. mov AX,SelLDT0 ; AX := offset DefLDT0 v segmentu GDT. mov TSSstrt.tLDT,AX ; TSSstrt.tLDT := offset DefLDT0 ; Nastaveni obsahu TSS procesu V86. mov AX,DS ; Napln AX obsahem segmentoveho reg. DS. mov TSSv86.tES,AX ; Datove segmentove registry TSS V86 mov TSSv86.tDS,AX ; budou ukazovat na stejny segment mov TSSv86.tFS,AX ; jako tento program. mov TSSv86.tGS,AX mov AX,SS ; Napln AX obsahem segmentoveho reg. SS. mov TSSv86.tSS,AX ; Zasobnik totozny s timto programem. mov AX,CS ; Napln AX obsahem segmentoveho reg. CS. mov TSSv86.tCS,AX ; Kod totozny s timto programem. mov TSSv86.tSS0,SelSupS ; Baze zasobniku v chran. rezimu. mov word ptr TSSv86.tESP0,1800h ; ESP pro uroven 0 je 1800h. mov TSSv86.tSS1,SelSupS ; Baze zasobniku v chran. rezimu. mov word ptr TSSv86.tESP1,1600h ; ESP pro uroven 1 je 1600h. mov TSSv86.tSS2,SelSupS ; Baze zasobniku v chran. rezimu. mov word ptr TSSv86.tESP2,1400h ; ESP pro uroven 2 je 1400h. xor EAX,EAX ; EAX := 0 mov AX,OFFSET ZacV86; EAX := OFFSET zacatku V86 procesu mov TSSv86.tEIP,EAX ; EIP v TSSv86:=OFFSET zac. V86 procesu mov AX,SP ; EAX := SP mov TSSv86.tESP,EAX ; ESP pro uroven 3 je totozne s SP. mov TSSv86.tEFLAGS,0100011001000000010b ; IOPL=3,IF=1,VM=1 ; FLAGS: VR NIOODITSZ A P C Nastaveni priznaku. mov TSSv86.IOmap,OffIOmp ; OFFSET mapy V/V bran procesu V86. mov TSSv86.tLDT,SelLDT3 ; Selektor LDT procesu V86. ; Nastaveni GDTR pred prepnutim do chraneneho rezimu. mov EAX,dsaddr ; EAX := DSx16 xor EBX,EBX ; EBX := 0 lea BX,DefGDT ; EBX := OFFSET DefGDT v ramci DS. add EBX,EAX ; EBX := Linearni adresa DefGDT mov dBase,EBX ; Naplnime pomocnou promennou pro mov dLimit,SzGDT-1 ; instrukci LGDT. lgdt QWORD PTR dLimit; Naplneni GDTR bazi a limitem GDT. ; Nastaveni IDTR pred prepnutim do chraneneho rezimu. xor EBX,EBX ; EBX := 0 lea BX,DefIDT ; EBX := OFFSET DefIDT v ramci DS. add EBX,EAX ; EBX := Linearni adresa DefIDT mov dBase,EBX ; Naplnime pomocnou promennou pro mov dLimit,SzIDT-1 ; instrukci LIDT. lidt QWORD PTR dLimit; Naplneni IDTR bazi a limitem IDT. ; Prepnuti do chraneneho rezimu. mov EAX,CR0 ; Precti stavajici obsah rid. reg. CR0. or AL,1 ; Nastav na jednicku bit 0 (Prot.Enable) mov CR0,EAX ; Napln CR0 novym obsahem. jp ZrusFrontu ; Kratky skok pro zruseni instrukcni ZrusFrontu: ; fronty predvybranych instrukci. ; Nastaveni registru LDTR pro prave aktivni startovaci proces. mov AX,SelLDT0 ; AX := selektor LDT pro uroven 0. lldt AX ; Naplneni LDT. ; Nastaveni obsahu vsech segmentovych registru. mov AX,SelSupD ; AX := selektor datoveho segmentu. mov DS,AX ; Vsechny datove segmentove registry mov ES,AX ; ukazuji na stejnou datovou oblast mov FS,AX ; jako v realnem rezimu. mov GS,AX mov AX,SelSupS ; AX := selektor segmentu se zasobnikem. mov SS,AX ; Napln SS selektorem zasobnik. segmentu. db 0EAh ; Proved instrukci pro naplneni CS: dw NaplnCS,SelSupC ; jmp FAR PTR SelSupC:NaplnCS NaplnCS: mov CX,SelStrt ; Naplnime TR selektorem startovaciho ltr CX ; procesu. ; Nastavime priznak NT v EFLAGS, aby IRET provedl prepnuti procesu. pushfd ; Momentalni obsah EFLAGS ziskame tak, pop EAX ; ze jej ulozime do zasobniku a vybereme. or AH,40h ; Obraz EFLAGS je v EAX, tam NT:=1. push EAX ; Obraz EFLAGS prepiseme do EFLAGS opet popfd ; pres zasobnik. iret ; IRET prepne na V86 proces podle zpetneho ukazatele TSS. ; Nyni jsme jiz v procesu V86 a muzeme pouzivat sluzby BIOSu i MS-DOSu. ZacV86: lea DX,Zprava ; DX := OFFSET textu: Prave byl zapnut... mov AH,9 ; Text vypiseme sluzbou MS-DOS cislo 9, int 21h ; ktera se aktivuje pres INT 21h. ; Ukoncime program s rezidentnim zustatkem podle pravidel MS-DOSu. mov DX,OFFSET Skupina:VelRezid ; DX := max. adresu rezident. add DX,20fh ; casti, zvetsi ji o tabulky a shr DX,4 ; a vydel 16. mov AX,3100h ; Rezidentne ukoncime sluzbou 31h int 21h ; ze skupiny sluzeb MS-DOSu. Instal ENDP ; Konec instalacni casti programu. CODE ENDS ; Konec instrukcniho segmentu. END Instal ; Startovaci bod je Instal.