; ============= 32-bit Extender for 16-bit DOS (v1.1) ============= ; Le fichier original se trouve dans \asm\ftp\snip\d32 ; ; Auteur : Benoit Papillault ; Date de creation: le 9/11/1996 ; Derniere modification: Dimanche 30 Mars 1997 ; Le point d'entree est d32 ; .seq .386p io_limit equ 2000h stack16_size equ 80h code segment public use32 assume cs:code extrn main:near, stack0:dword extrn irq0:near, irq1:near, irq2:near, irq3:near extrn irq4:near, irq5:near, irq6:near, irq7:near extrn irq8:near, irq9:near, irq10:near, irq11:near extrn irq12:near, irq13:near, irq14:near, irq15:near public base_sel, code_sel, data_sel, env_sel, ext_sel public flat_code_sel, flat_data_sel, psp_sel, video_sel base_sel equ base_desc - GDT code_sel equ code_desc - GDT data_sel equ data_desc - GDT code16_sel equ code16_desc - GDT data16_sel equ data16_desc - GDT env_sel equ env_desc - GDT ext_sel equ ext_desc - GDT flat_code_sel equ flat_code_desc - GDT flat_data_sel equ flat_data_desc - GDT psp_sel equ psp_desc - GDT video_sel equ video_desc - GDT public V86_es, V86_ds, V86_fs, V86_gs align 4 V86_es dd code V86_ds dd code V86_fs dd 0 V86_gs dd 0a000h V86_ss dd stack16 V86_sp dd stack16_size TSS_esp0 dd (32+16)*8+4 public psp_segment, base_segment, base_limit public ext_base, ext_limit, fpu_present psp_segment dw ? base_segment dw ? base_limit dd ? ext_base dd 100000h ext_limit dd ? fpu_present db 0 ; ============= V86 interupt call ============= ; sur la pile figure le numero de l'interruption demande multipliee par 4 align 4 public int_16 int_16 proc near pushfd push gs fs ds es ebx eax mov ax,data_sel mov ds,ax mov ax,flat_data_sel mov fs,ax mov eax,ds:TSS_esp0 push dword ptr fs:eax[4] push dword ptr fs:eax[0] mov fs:eax[0],esp mov fs:eax[4],ss push ds:V86_gs ds:V86_fs ds:V86_ds ds:V86_es mov ebx,ds:V86_ss push ebx shl ebx,4 mov eax,ds:V86_sp sub eax,6 push eax add ebx,eax mov word ptr fs:ebx[0],offset int_? mov word ptr fs:ebx[2],code mov eax,ss:esp[14*4] and ah,NOT 42h push eax popfd mov fs:ebx[4],ax or eax,23000h push eax ; push flag with VM bit set and ; a IOPL of three mov ebx,ss:esp[17*4] ; ebx = intno*4 push onto the stack ; before calling int_16 movzx eax,word ptr fs:ebx[2] push eax ; push code segment of requested ; interrupt intno mov ax,word ptr fs:ebx[0] push eax ; push segment offset of requested ; interrupt intno ; On restore eax et ebx a leur valeur a l'entree de la fonction mov eax,ss:esp[11*4] mov ebx,ss:esp[12*4] iretd int_?: int ? int_16 endp ; ============= Virtual 8086 mode monitor ============= v86_monitor: ; On teste le bit VM du registre EFLAGS (qui est sur la pile) ; pour savoir si l'exception provient du code en mode V86 test byte ptr ss:esp[3*4+2],2 jz exc13 add esp,4 ; on elimine le code d'erreur push ebx eax mov ax,flat_data_sel mov ds,ax ; On recupere la valeur de CS qui est sur la pile movzx ebx,word ptr ss:esp[3*4] cmp bx,code je V86_exit shl ebx,4 add ebx,ss:esp[2*4] ; ebx est l'adresse de l'instruction qui a declenchee l'exception inc dword ptr ss:esp[2*4] mov ah,ds:ebx[0] mov al,3 ; On teste l'interruption INTO (int 4h) cmp ah,0cch je short V86_int inc eax ; On teste l'interruption 3h cmp ah,0ceh je short V86_int ; On teste le cas d'une interruption avec argument cmp ah,0cdh jne short V86_exc inc dword ptr ss:esp[2*4] mov al,ds:ebx[1] cmp al,15h jne short V86_int cmp byte ptr ss:esp[1],87h je emul_15_87 ; ------------- Emulate INT intruction ------------- ; le numero de l'interruption a emuler est dans al V86_int: push ecx movzx ebx,al shl ebx,2 mov ecx,ds:ebx[0] movzx ebx,word ptr ss:esp[7*4] shl ebx,4 sub word ptr ss:esp[6*4],6 add ebx,ss:esp[6*4] mov ax,ss:esp[5*4] mov ds:ebx[4],ax and ah,NOT 3 mov ss:esp[5*4],ax mov ax,ss:esp[4*4] mov ds:ebx[2],ax mov ax,ss:esp[3*4] mov ds:ebx[0],ax mov ss:esp[3*4],cx shr ecx,16 mov ss:esp[4*4],cx pop ecx eax ebx iretd V86_exc: mov dx,offset err_v86_inv jmp exception ; ------------- Leave V86 Interrupt call ------------- V86_exit: mov ax,data_sel mov ds,ax pop eax ebx mov ss:esp[11*4],eax mov ss:esp[12*4],ebx add esp,8 pop eax mov ss:esp[14*4],al pop ds:V86_sp ds:V86_ss pop ds:V86_es ds:V86_ds ds:V86_fs ds:V86_gs mov ax,flat_data_sel mov ebx,ds:TSS_esp0 mov ds,ax pop dword ptr ds:ebx[0] pop dword ptr ds:ebx[4] pop eax ebx es ds fs gs popfd ret 4 ; ------------- Emulate 'MOVE BLOCK' BIOS call ------------- emul_15_87: cmp ecx,8000h ja short err_15_87 push edi esi ecx movzx eax,word ptr ss:esp[10*4] shl eax,4 movzx ebx,si add eax,ebx mov esi,ds:eax[10h + 7] shl esi,24 mov ebx,ds:eax[10h + 2] and ebx,0ffffffh add esi,ebx mov edi,ds:eax[18h + 7] shl edi,24 mov ebx,ds:eax[18h + 2] and ebx,0ffffffh add edi,ebx mov ax,ds mov es,ax cld shr ecx,1 rep movsd pop ecx esi edi mov byte ptr ss:esp[1],0 and byte ptr ss:esp[4*4],NOT 1 jmp short exit_15_87 err_15_87: mov byte ptr ss:esp[1],3 or byte ptr ss:esp[4*4],1 exit_15_87: pop eax ebx iretd ; ============= exception entries ============= exc0: mov dx,offset exc_0 jmp short exception exc1: mov dx,offset exc_1 jmp short exception exc2: mov dx,offset exc_2 jmp short exception exc3: mov dx,offset exc_3 jmp short exception exc4: mov dx,offset exc_4 jmp short exception exc5: mov dx,offset exc_5 jmp short exception exc6: mov dx,offset exc_6 jmp short exception exc7: mov dx,offset exc_7 jmp short exception exc8: mov dx,offset exc_8 jmp short exception exc9: mov dx,offset exc_9 jmp short exception exc10: mov dx,offset exc_10 jmp short exception exc11: mov dx,offset exc_11 jmp short exception exc12: mov dx,offset exc_12 jmp short exception exc13: mov dx,offset exc_13 jmp short exception exc14: mov dx,offset exc_14 jmp short exception exc15: mov dx,offset exc_15 jmp short exception exc16: mov dx,offset exc_16 jmp short exception exc17: mov dx,offset exc_17 jmp short exception exc18_31: mov dx,offset exc_18_31 exception: mov ebp,'EXCE' ; ! jmp code16_sel:exit16 ! db 0eah dd exit16 dw code16_sel ; ============= Non Maskable Interrupt ============= NMI: call check_int push 2*4 call int_16 iretd ; ============= Interrupt entries ============= int0: push offset irq0 jmp short check_int int1: push offset irq1 jmp short check_int int2: push offset irq2 jmp short check_int int3: push offset irq3 jmp short check_int int4: push offset irq4 jmp short check_int int5: push offset irq5 jmp short check_int int6: push offset irq6 jmp short check_int int7: push offset irq7 jmp short check_int int8: push offset irq8 jmp short check_int int9: push offset irq9 jmp short check_int int10: push offset irq10 jmp short check_int int11: push offset irq11 jmp short check_int int12: push offset irq12 jmp short check_int int13: push offset irq13 jmp short check_int int14: push offset irq14 jmp short check_int int15: push offset irq15 check_int: test byte ptr ss:esp[3*4+2],2 jz short not_V86 push eax mov ax,data_sel mov ds,ax mov eax,ss:esp[5*4] mov ds:V86_sp,eax mov eax,ss:esp[6*4] mov ds:V86_ss,eax pop eax mov es,ss:esp[14*4] mov ds,ss:esp[15*4] mov fs,ss:esp[16*4] mov gs,ss:esp[17*4] not_V86: ret code ends ; ============= Startup code ============= code16 segment use16 assume cs:code16 d32 proc .8086 ; ============= Check for 386 or above ============= push cs pop ds pushf pushf pop ax xor ah,40h push ax popf pushf pop bx popf cmp ax,bx je short NT_found mov dx,offset err_no_386 jmp err_exit NT_found: .386p ; ============= Check for V86 mode ============= smsw ax test al,1 jz short real_mode mov dx,offset err_v86 jmp err_exit real_mode: ; ============= Store environment and psp segments ============= movzx eax,word ptr es:[2ch] shl eax,4 mov ds:env_desc[2],ax shr eax,16 mov byte ptr ds:env_desc[4],al mov ax,es movzx ebx,word ptr es:[02h] mov cx,code mov es,cx mov es:psp_segment,ax shl eax,4 mov ds:psp_desc[2],ax shr eax,16 mov byte ptr ds:psp_desc[4],al ; ============= Check for a Floating Point Unit ============= fninit fnstsw ds:fp_status cmp byte ptr ds:fp_status,0 jnz short no_fpu fnstcw ds:fp_status mov ax,ds:fp_status and ax,103fh cmp ax,3fh jne short no_fpu mov es:fpu_present,1 no_fpu: ; ============= Store code and code16 segments ============= mov ax,es shl eax,4 sub ds:code_desc,ax sub ds:data_desc,ax mov ds:code_desc[2],ax mov ds:data_desc[2],ax shr eax,16 mov byte ptr ds:code_desc[4],al mov byte ptr ds:data_desc[4],al sub byte ptr ds:code_desc[6],al sub byte ptr ds:data_desc[6],al mov ax,cs shl eax,4 add dword ptr ds:GDTR[2],eax mov ds:code16_desc[2],ax mov ds:data16_desc[2],ax shr eax,16 mov byte ptr ds:code16_desc[4],al mov byte ptr ds:data16_desc[4],al ; ============= Store free base segment ============= mov ax,stack16 add ds:IDT_seg,ax add ax,(stack16_size + 48*8 + 68h + io_limit + 15)/16 sub bx,ax ja short mem_ok mov dx,offset err_mem jmp err_exit mem_ok: mov es:base_segment,ax shl eax,4 mov ds:base_desc[2],ax shr eax,16 mov byte ptr ds:base_desc[4],al shl ebx,4 mov es:base_limit,ebx mov ds:base_desc,bx shr ebx,16 mov byte ptr ds:base_desc[6],bl ; ============= Enable the A20 gate & Allocate extended memory ============ mov ax,4300h int 2fh cmp al,80h jnz no_xms ; ------------- Enable through XMS driver ------------- push es mov ax,4310h int 2fh mov word ptr ds:XMS_driver,bx mov word ptr ds:XMS_driver[2],es pop es mov ah,05h call ds:XMS_driver cmp ax,1 jz short a20_XMS_enabled mov dx,offset err_xms_a20 jmp err_exit a20_XMS_enabled: ; ------------- Allocate through XMS driver ------------- mov ah,08h call ds:XMS_driver mov dx,ax shl eax,10 mov es:ext_limit,eax mov ah,09h call ds:XMS_driver cmp ax,1 je short alloc_ok mov dx,offset err_xms_alloc jmp err_exit alloc_ok: mov ds:XMS_handle,dx mov ah,0ch call ds:XMS_driver cmp ax,1 je short lock_ok mov dx,offset err_xms_lock jmp err_exit lock_ok: mov ds:ext_desc[2],bx mov byte ptr ds:ext_desc[4],dl mov byte ptr ds:ext_desc[7],dh mov word ptr es:ext_base,bx mov word ptr es:ext_base[2],dx mov ax,word ptr es:ext_limit jmp ext_allocated ; ------------- Enable through 8042 (keyboard controller) ------------- wait_status_bit1 proc xor cx,cx wsb10: in al,64h test al,2 jz short wsb11 loop wsb10 jmp short time_out wsb11: ret wait_status_bit1 endp no_xms: cli mov al,0adh out 64h,al call wait_status_bit1 mov al,0d0h out 64h,al xor cx,cx wsb00: in al,64h test al,1 jnz short wsb01 loop wsb00 time_out: mov dx,offset err_8042_a20 jmp err_exit wsb01: in al,60h push ax mov al,0d1h out 64h,al call wait_status_bit1 pop ax or al,2 out 60h,al call wait_status_bit1 mov al,0aeh out 64h,al ; ------------- Check if A20 really is enabled ------------- xor ax,ax mov fs,ax dec ax mov gs,ax mov al,fs:0 cmp al,gs:10h jne short a20_8042_enabled not al mov fs:0,al cmp al,gs:10h not al mov fs:0,al jnz short a20_8042_enabled mov dx,offset err_8042_a20 jmp err_exit a20_8042_enabled: ; ------------- Get extended memory size (BIOS) ------------- mov ah,88h int 15h shl eax,10 mov es:ext_limit,eax ext_allocated: add eax,4095 shr eax,12 dec eax mov ds:ext_desc,ax shr eax,16 add byte ptr ds:ext_desc[6],al ; ============= Build IDT & TSS ============= push es mov es,ds:IDT_seg xor edi,edi mov eax,code_sel * 65536 + offset exc0 mov ebx,8e00h ; 386 Interrupt Gate mov cx,19 loop1: stosd mov es:di[0],ebx add di,4 add ax,offset exc1 - exc0 loop loop1 ; L'exception 13 (General Protection Fault) est redirigee vers V86_monitor mov word ptr es:di[-6*8],offset V86_monitor ; L'exception 2 (NMI) est redirigee vers la fonction NMI mov word ptr es:di[-17*8],offset NMI mov cx,13 loop2: stosd mov es:di[0],ebx add di,4 loop loop2 mov ax,offset int0 mov cx,16 loop3: stosd mov es:di[0],ebx add ax,offset int1 - int0 add di,4 loop loop3 xor eax,eax mov cx,(68h + io_limit)/4 rep stosd mov byte ptr es:di[0],-1 mov word ptr es:48*8 + 66h,68h mov ax,es shl eax,4 mov dword ptr ds:IDTR[2],eax pop es add es:TSS_esp0,eax add ds:TSS_desc[2],ax shr eax,16 mov byte ptr ds:TSS_desc[4],al ; ============= Set Programmable Interrupt Controller ============= cli in al,21h mov ds:irq_mask,al in al,0a1h mov ds:irq_mask[1],al mov bx,2820h call set_PIC mov al,ds:irq_mask out 21h,al mov al,ds:irq_mask[1] out 0a1h,al ; ============= Enter protected mode ============= lidt qword ptr ds:IDTR lgdt qword ptr ds:GDTR mov eax,CR0 or al,21h mov CR0,eax ; ! jmp code16_sel:prot ! db 0eah dw prot, code16_sel ; ============= Protected mode ============= prot: mov ax,data_sel mov es,ax mov ds,ax mov ss,ax mov ax,flat_data_sel mov fs,ax mov ax,video_sel mov gs,ax mov esp,offset stack0 mov ax,TSS_desc - GDT ltr ax sti ; ! call code_sel:main ! db 66h, 67h, 09ah dd offset main dw code_sel ; ============= Leave protected mode ============= exit16: cli mov ax,data16_sel mov es,ax mov ds,ax mov fs,ax mov gs,ax mov ss,ax lidt ds:DOS_IDTR mov eax,CR0 and al,NOT 21h mov CR0,eax ; ! jmp code16:real ! db 0eah dw real, code16 ; ============= Real mode ============= real: mov ax,0b800h mov es,ax mov ax,code16 mov ds,ax mov ax,stack16 mov ss,ax mov esp,offset stack16_size ; ============= Reset PIC ============= mov bx,7008h call set_PIC mov al,ds:irq_mask out 21h,al mov al,ds:irq_mask[1] out 0a1h,al mov al,20h out 20h,al out 0a0h,al sti ; ============= Exception exit? ============= cmp ebp,'EXCE' jne short exit ; mov ah,0fh ; int 10h ; cmp al,7 ; je short mda ; mov al,3 ;mda: ; mov ah,0 ; int 10h err_exit: mov ah,9 int 21h exit: cmp word ptr ds:XMS_driver,0 je short no_xms_driver mov ah,0dh mov dx,ds:XMS_handle call ds:XMS_driver mov ah,0ah call ds:XMS_driver no_xms_driver: mov ax,4c00h int 21h d32 endp set_PIC proc mov al,11h out 20h,al out 0a0h,al mov al,bl out 21h,al mov al,bh out 0a1h,al mov al,4h out 21h,al mov al,2h out 0a1h,al mov al,1h out 21h,al out 0a1h,al ret set_PIC endp ; errors err_8042_a20 db '8042 : Failed to enable A20 gate',10,36 err_no_386 db '80x86 : This program requires a i386 microprocessor',10,36 err_mem db 'Memory : Out of base memory',10,36 err_v86 db 'V86 : Already in protected mode',10,36 err_v86_inv db 'v86 monitor : Unknown opcode',10,36 err_xms_a20 db 'XMS : Failed to enable A20 gate',10,36 err_xms_alloc db 'XMS : Failed to allocate XMS memory',10,36 err_xms_lock db 'XMS : Failed to lock XMS memory',10,36 ; exceptions exc_0 db '0 : Divide Error',10,36 exc_1 db '1 : Debug Exception',10,36 exc_2 db '2 : NMI Interrupt',10,36 exc_3 db '3 : One Byte Interrupt',10,36 exc_4 db '4 : Interrupt on Overflow',10,36 exc_5 db '5 : Array Bounds Check',10,36 exc_6 db '6 : Invalid OP-Code',10,36 exc_7 db '7 : Device Not Available',10,36 exc_8 db '8 : Double fault',10,36 exc_9 db '9 : Coprocessor Segment Overrun',10,36 exc_10 db '10 : Invalid TSS',10,36 exc_11 db '11 : Segment Not Present',10,36 exc_12 db '12 : Stack Fault',10,36 exc_13 db '13 : General Protection Fault',10,36 exc_14 db '14 : Page Fault',10,36 exc_15 db '15 : Reserved',10,36 exc_16 db '16 : Floating Point Error',10,36 exc_17 db '17 : Alignment Check Interrupt',10,36 exc_18_31 db '18-31 : Reserved',10,36 align 4 irq_mask db 0, 0 fp_status dw -1 IDT_seg dw stack16_size/16 XMS_driver dd 0 XMS_handle dw 0 align 8 GDT dd 0, 0 base_desc dw ? dw ? db ? db 92h, ?, 0 code_desc dw 0ffffh dw ? db ? db 9ah, 49h, 0 data_desc dw 0ffffh dw ? db ? db 92h, 09h, 0 code16_desc dw 0ffffh dw ? db ? db 9ah, 0, 0 data16_desc dw 0ffffh dw ? db ? db 92h, 0, 0 env_desc dw 003ffh dw ? db ? db 92h, 0, 0 ext_desc dw 00000h dw 0 db 10h db 92h, 080h, 0 flat_code_desc dw 0ffffh dw 0 db 0 db 9ah, 08fh, 0 flat_data_desc dw 0ffffh dw 0 db 0 db 92h, 08fh, 0 psp_desc dw 000ffh dw ? db ? db 92h, 0, 0 video_desc dw 0ffffh dw 0 db 0ah db 92h, 01h, 0 TSS_desc dw 68h + io_limit dw 180h db ? db 89h, 0, 0 ; 486 TSS descriptor (System Descriptor) GDTR dw $ - GDT dd offset GDT dw 0 IDTR dq 17fh DOS_IDTR dq 3ffh code16 ends stack16 segment stack use16 db stack16_size dup (?) stack16 ends end d32