Metropoli BBS
VIEWER: pmode.asm MODE: TEXT (CP437)
; PMODE v2.1232 raw, DPMI, VCPI, & XMS compliant protected mode header.
; By Tran (a.k.a. Thomas Pytel) of Renaissance.

        .386p

LOWMIN          = 0             ; minimum free low memory (in K)
EXTMIN          = 0             ; minimum free extended memory (in K)
STAKLEN         = 100h          ; stack size in para (keep below or = 1000h)
STAKSAFE        = 20h           ; safe stack space in para

code16  segment para public use16
code16  ends
code32  segment para public use32
code32  ends
codeend segment para stack use32 'stack'
codeend ends

;▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
; Real mode and 16bit code
;▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
code16  segment para public use16
        assume cs:code16, ds:code16
        org 0

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; 16 bit common system data
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
errmsg0         db      '386 or better not detected!!!',7,'$'
errmsg1         db      'Not enough low memory!!!',7,'$'
errmsg2         db      'This system is already in V86 mode!!!',7,'$'
errmsg3         db      'Not enough extended memory!!!',7,'$'
errmsg4         db      'Couldn''t enable A20 gate!!!',7,'$'
errmsg5         db      'Extended memory allocation failure. (weird eh???)',7,'$'

nullint         db      0cfh            ; IRET instruction
exitrout        dw      exit            ; exit routine, modified if XMS, VCPI

savedstakoff    dw      ?               ; current saved stack offset
savedstakseg    dw      ?               ; current saved stack segment

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; 16 bit common system code
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
;═════════════════════════════════════════════════════════════════════════════
intreal:                                ; real mode int, load FS and GS here
        pushf
;-----------------------------------------------------------------------------
callreal:                               ; real mode call, load FS and GS here
        push cs
        push offset icreald
        mov fs,cs:v86r_fs
        mov gs,cs:v86r_gs
        mov eax,cs:v86r_eax
        mov ecx,cs:v86r_ecx
        mov edx,cs:v86r_edx
        mov ebx,cs:v86r_ebx
        mov esi,cs:v86r_esi
        mov edi,cs:v86r_edi
        mov ebp,cs:v86r_ebp
;-----------------------------------------------------------------------------
icreal:                                 ; real mode int, call, or IRQ
        db 66h,68h              ; PUSH destination addx
icrealm1        dd      ?       ;
icrealm0        db      ?       ; CLI or STI
        retf
;-----------------------------------------------------------------------------
icreald:                                ; done with real int or call
        cli
        pushf
        pop cs:v86r_flags
        mov cs:v86r_eax,eax
        mov cs:v86r_ecx,ecx
        mov cs:v86r_edx,edx
        mov cs:v86r_ebx,ebx
        mov cs:v86r_esi,esi
        mov cs:v86r_edi,edi
        mov cs:v86r_ebp,ebp
        mov cs:v86r_ds,ds
        mov cs:v86r_es,es
        mov cs:v86r_fs,fs
        mov cs:v86r_gs,gs
icreald2:
        mov ax,cs
        mov ds,ax
icrealm2        label word              ; return to pmode modifiable to JMP
;-----------------------------------------------------------------------------
        mov ebx,ds:cp_savedstakoff      ; DPMI return to pmode
        mov dx,ds:dp_savedstaksel
        mov edi,offset dp_int3_d
        mov si,ds:_selcode
        mov cx,dx
        mov ax,ds:_seldata
        jmp ds:d_switchaddx
;-----------------------------------------------------------------------------
VICREAL1D=(($-(icrealm2+2))shl 8)+0ebh
v_icreal1d:                             ; VCPI return to pmode from safe
        mov edi,offset cp_int3_d
;-----------------------------------------------------------------------------
; EDI=offset to jump to in code32
v_switchtopmode:                        ; VCPI switch to pmode
        mov ds:v_ss_dest,edi
        mov esi,offset v_ss_cr3
        add esi,ds:_code16a
        mov ax,0de0ch
        int 67h
;-----------------------------------------------------------------------------
CICREAL1D=(($-(icrealm2+2))shl 8)+0ebh
c_icreal1d:                             ; custom return to pmode from safe
	mov edi,offset cp_int3_d
;-----------------------------------------------------------------------------
; EDI=offset in pmode to jump to
c_retpmode:                             ; reenter 32bit pmode
        lgdt fword ptr c_gdt32addx      ; set up pmode GDT and IDT
	lidt fword ptr c_idt32addx
	mov ds:gdt32task[5],89h 	; set task as not busy
        mov eax,cr0                     ; switch to pmode
        or al,1
        mov cr0,eax
        db 0eah
        dw $+4,20h
        mov ax,30h                      ; load task register
        ltr ax
	jmp c_gotopmode
;-----------------------------------------------------------------------------
if ($-(icrealm2+2)) gt 127
  err
endif
CICREAL0D=(($-(icrealm2+2))shl 8)+0ebh
c_icreal0d:                             ; return to pmode from normal
        int 0ffh
;═════════════════════════════════════════════════════════════════════════════
int32:                                  ; real mode INT32: EDX=off
        pushad
        push ds es fs gs
        cli
        mov ax,cs
        mov ds,ax
        mov ds:p_cpmodem0,edx
        mov al,[esp+45]
        shr al,1
        and al,1
        add al,0fah
        mov ds:p_cpmodem1,al
        push savedstakoff
        push savedstakseg
        movzx ebx,ds:nextmodestack
        lea eax,[ebx-STAKSAFE*16]
        mov ds:nextmodestack,ax
        add ebx,ds:realstackbase
        mov savedstakseg,ss
int32m0         label   word            ; jump to pmode, modifiable
;-----------------------------------------------------------------------------
        sub sp,ds:dp_savelen
        mov savedstakoff,sp
        mov ax,ss                       ; DPMI jump to pmode
        mov es,ax
        mov di,sp
        xor al,al
        call d_saveaddx
        mov ax,ds:_seldata
        mov cx,ax
        mov dx,ax
        mov edi,offset p_cpmode
        mov si,ds:_selcode
        jmp ds:d_switchaddx
;-----------------------------------------------------------------------------
VINT32=(($-(int32m0+2))shl 8)+0ebh
v_int32:                                ; VCPI call pmode
        push ds:p_cpmodem2
        mov ds:p_cpmodem2,VCPMODED
        mov savedstakoff,sp
        mov edi,offset p_cpmode1
        jmp v_switchtopmode
;-----------------------------------------------------------------------------
if ($-(int32m0+2)) gt 127
  err
endif
CINT32=(($-(int32m0+2))shl 8)+0ebh
c_int32:                                ; raw/XMS call pmode
        push ds:p_cpmodem2
        mov ds:p_cpmodem2,CCPMODED
        mov savedstakoff,sp
        mov edi,offset p_cpmode1
        jmp c_retpmode
;═════════════════════════════════════════════════════════════════════════════
int32d0:                                ; DPMI done with pmode call
        mov di,sp
        mov al,1
        call d_saveaddx
        add sp,ds:dp_savelen
;-----------------------------------------------------------------------------
int32d2:                                ; done from all
        pop savedstakseg
        pop savedstakoff
        add ds:nextmodestack,STAKSAFE*16
        mov bx,ds:v86r_flags
        mov ax,[esp+44]
        and ax,not 8d5h
        and bx,8d5h
        or ax,bx
        mov [esp+44],ax
        pop gs fs es ds
        popad
        iret
;═════════════════════════════════════════════════════════════════════════════
int32d1:                                ; VCPI done with pmode call
        mov ss,savedstakseg
        pop ds:p_cpmodem2
        jmp int32d2
;═════════════════════════════════════════════════════════════════════════════
int32d3:                                ; raw/XMS done with pmode call
        mov c_retreal0m0,offset c_sicreal
        mov ax,cs
        mov ds,ax
        movzx esp,savedstakoff
        mov ss,savedstakseg
        pop ds:p_cpmodem2
        jmp int32d2
;─────────────────────────────────────────────────────────────────────────────
chek_VCPI:                              ; Chek for VCPI
        mov dx,offset v_emmname
        mov ax,3d00h
        int 21h
        jc short chekVCPIa
        mov bx,ax
        mov ax,4400h
        int 21h
        jc short chekVCPIa
        test dh,80h
        jz short chekVCPIa
        mov ax,4407h
        int 21h
        mov dl,al
        mov ah,3Eh
        int 21h
        cmp dl,0FFh
        jne short chekVCPIa
        mov bx,1
        mov ah,43h
        int 67h
;       or ah,ah
;       jnz short chekVCPIa
        mov v_emshandle,dx
        mov ax,0de00h
        int 67h
        or ah,ah
        clc
        jz short chekVCPId
        mov ah,45h
        int 67h
chekVCPIa:
        stc
chekVCPId:
        ret
;─────────────────────────────────────────────────────────────────────────────
chek_processor:                         ; Detect if current processor 386
        pushf
        xor ah,ah
        push ax
        popf
        pushf
        pop ax
        and ah,0f0h
        cmp ah,0f0h
        je short detectno386
        mov ah,0f0h
        push ax
        popf
        pushf
        pop ax
        and ah,0f0h
        jz short detectno386
        popf
        ret
detectno386:
        mov dx,offset errmsg0
        jmp short exit16err
;─────────────────────────────────────────────────────────────────────────────
chek_V86:                               ; Chek if already in V86 mode
        smsw ax
        test al,1
        mov dx,offset errmsg2
        jnz short exit16err
        ret
;─────────────────────────────────────────────────────────────────────────────
pregetlomem:                            ; Get low memory or abort
        add eax,ds:_lomembase
        mov ebx,ds:_lomemtop
        cmp eax,ebx
        ja short pregetlomema
        mov ecx,eax
        xchg eax,ds:_lomembase
        sub ebx,ecx
        cmp ebx,LOWMIN*1024
        jb short pregetlomema
        ret
pregetlomema:
        mov dx,offset errmsg1
;─────────────────────────────────────────────────────────────────────────────
exit16err:                              ; Exit program with message
        mov ah,9
        int 21h
        jmp exitrout
;-----------------------------------------------------------------------------
exit:                                   ; Guess what???
        mov ah,4ch
        int 21h
;═════════════════════════════════════════════════════════════════════════════
start16:                                ; Program begins here
        cli
        cld
        push cs
        pop ds

        call chek_processor             ; is it at least a 386¿

        mov ax,es                       ; set up a bunch of pointers
        movzx eax,ax
        shl eax,4
        mov ds:_pspa,eax
        mov eax,code16
        shl eax,4
        mov ds:_code16a,eax
        or dword ptr ds:gdt32code16[2],eax
        or dword ptr ds:gdt32data16[2],eax
        mov ebx,code32
        shl ebx,4
        mov ds:_code32a,ebx
        or dword ptr ds:gdt32code32[2],ebx
        or dword ptr ds:gdt32data32[2],ebx
        add dword ptr ds:c_gdt32addx[2],ebx
        mov eax,codeend
        shl eax,4
        sub eax,ebx
        mov ds:_lomembase,eax
        mov ds:realstackbase,eax
        movzx eax,word ptr es:[2]
        shl eax,4
        sub eax,ebx
        mov ds:_lomemtop,eax

        mov eax,STAKLEN*16              ; get stack memory
        call pregetlomem

        push es                         ; save PSP seg (DPMI chek kills ES)
        pop fs

        mov ax,1687h                    ; chek for DPMI
        int 2fh
        or ax,ax
        jz d_start

        call chek_VCPI                  ; chek for VCPI
        jnc v_start

        call chek_V86                   ; chek for V86 mode

        mov ax,4300h                    ; chek for XMS
        int 2fh
        cmp al,80h
        je x_start

        jmp c_start                     ; custom system start
;─────────────────────────────────────────────────────────────────────────────
enableA20:                              ; hardware enable gate A20
        xor ax,ax
        mov fs,ax
        dec ax
        mov gs,ax
        call testA20
        je short enableA20done
        in al,92h                       ; PS/2 A20 enable
        or al,2
        jmp short $+2
        jmp short $+2
        jmp short $+2
        out 92h,al
        call testA20
        je short enableA20done
        call enableA20o1                ; AT A20 enable
        jnz short enableA20wait
        mov al,0d1h
        out 64h,al
        call enableA20o1
        jnz short enableA20wait
        mov al,0dfh
        out 60h,al
        push offset enableA20wait
enableA20o1:
        mov ecx,20000h
enableA20o1l:
        jmp short $+2
        jmp short $+2
        jmp short $+2
        in al,64h
        test al,2
        loopnz enableA20o1l
enableA20done:
        ret
;-----------------------------------------------------------------------------
enableA20wait:                          ; wait for A20
        mov al,36h
        out 43h,al
        xor al,al
        out 40h,al
        out 40h,al
        mov cx,800h
enableA20waitl0:
        call testA20
        je enableA20done
        in al,40h
        in al,40h
        mov ah,al
enableA20waitl1:
        in al,40h
        in al,40h
        cmp al,ah
        je enableA20waitl1
        loop enableA20waitl0
        mov dx,offset errmsg4
        jmp exit16err
;-----------------------------------------------------------------------------
testA20:                                ; Test for enabled A20
        mov al,fs:[0]
        mov ah,al
        not al
        mov gs:[10h],al
        cmp ah,fs:[0]
        mov fs:[0],ah
        ret
;─────────────────────────────────────────────────────────────────────────────
; BL=low PIC val, BH=high PIC val
setintslots:                            ; set int nums in table to PIC vals
        mov edi,offset ds:intslottbl
        mov cl,8
setintslotsl0:
        mov [di],bl
        inc di
        inc bl
        dec cl
        jnz setintslotsl0
        mov cl,8
setintslotsl1:
        mov [di],bh
        inc di
        inc bh
        dec cl
        jnz setintslotsl1
        ret

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; 16 bit DPMI system data
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
d_errmsg0       db      'DPMI host is not 32bit!!!',7,'$'
d_errmsg1       db      'Ran out of DPMI descriptors!!!',7,'$'
d_errmsg2       db      'Couldn''t set DPMI descriptors as needed!!!',7,'$'

d_enterpmode    dw      ?,?             ; DPMI switch to pmode addx
d_pspsel        dw      ?               ; stupid PSP selector
d_oldenvsegsel  dw      ?               ; stupid selector we dont want

d_switchaddx    dd      ?               ; switch to pmode addx
d_saveaddx      dd      ?               ; save/restore state addx

d_nintoff       dd      offset dp_irq0,offset dp_irq1,offset dp_irq2,offset dp_irq3
                dd      offset dp_irq4,offset dp_irq5,offset dp_irq6,offset dp_irq7
                dd      offset dp_irq8,offset dp_irq9,offset dp_irqa,offset dp_irqb
                dd      offset dp_irqc,offset dp_irqd,offset dp_irqe,offset dp_irqf
                dd      offset dp_int33,offset dp_int32,offset dp_int33,offset dp_int32
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; 16 bit DPMI system code
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
;═════════════════════════════════════════════════════════════════════════════
d_retreal:                              ; Return to real mode
        mov ax,205h                     ; restore all int vektorz needed
        mov edi,19
d_retreall0:
        mov bl,ds:intslottbl[edi]
        lea ebp,[edi*2+edi]
        mov edx,dword ptr ds:dp_ointbuf[ebp*2]
        mov cx,word ptr ds:dp_ointbuf[ebp*2+4]
        int 31h
        sub di,1
        jnc d_retreall0
        jmp short d_exit
;─────────────────────────────────────────────────────────────────────────────
d_exit16err:                            ; DPMI Exit with error message
        mov ds:v86r_ds,code16
        mov ds:v86r_ah,9
        mov ax,300h
        mov bx,21h
        xor cx,cx
        mov edi,offset ds:v86r_edi
        push ds
        pop es
        int 31h
;─────────────────────────────────────────────────────────────────────────────
d_exit:                                 ; DPMI exit to real mode
        mov es,d_pspsel                 ; restore env selector
        mov ax,d_oldenvsegsel
        mov es:[2ch],ax
        jmp exit
;═════════════════════════════════════════════════════════════════════════════
d_start:                                ; Start in a crappy DPMI system
        or ds:_sysbyte0,3               ; set system type DPMI byte

        test bl,1                       ; must be 32bit DPMI
        mov dx,offset d_errmsg0
        jz exit16err

        mov d_enterpmode[0],di          ; store enter addx
        mov d_enterpmode[2],es
        push word ptr fs:[2ch]          ; preserve old env seg

        movzx eax,si                    ; get mem for DPMI blok
        shl eax,4
        call pregetlomem
        shr eax,4
        add ax,code32
        mov es,ax

        mov ax,1                        ; switch to pmode
        call dword ptr d_enterpmode
        cli                             ; I don't trust DPMI
        mov dx,offset d_errmsg1
        jc exit16err
        mov ds:v86r_dx,dx               ; prepare for abort maybe
        pop ax                          ; swap old env seg with selector
        xchg ax,es:[2ch]
        mov d_oldenvsegsel,ax
        mov d_pspsel,es                 ; store stupid selectors
        mov ds:data16sel,ds
        mov ds:code16sel,cs
        mov ds:code16off,offset d_retreal       ; set return to real mode addx
        mov ds:_setirqvect,offset dp_setirqvect ; modify some crap
        mov ds:_getirqvect,offset dp_getirqvect

        push ds                         ; no more need for PSP
        pop es
        mov ax,3                        ; get selector increment value
        int 31h
        mov bx,ax
        xor ax,ax                       ; get 3 needed descriptors
        mov cx,3
        int 31h
        jc d_exit16err

        mov si,ax                       ; set up descriptors
        mov ds:_selcode,ax
        lea ecx,[eax+ebx]
        mov ds:_seldata,cx
        lea ebp,[ecx+ebx]
        mov ds:_selzero,bp
        mov ds:v86r_dx,offset d_errmsg2
        mov ax,0ch                      ; set descriptors from GDT
        mov bx,si
        mov edi,offset ds:gdt32code32
        or byte ptr [edi+5],60h
        int 31h
        jc d_exit16err
        mov bx,cx
        mov edi,offset ds:gdt32data32
        or byte ptr [edi+5],60h
        int 31h
        jc d_exit16err
        mov bx,bp
        mov edi,offset ds:gdt32zero32
        or byte ptr [edi+5],60h
        int 31h
        jc d_exit16err

        mov es,cx                       ; ES, FS, and GS what they should be
        mov fs,cx
        mov gs,bp

        mov edi,ds:_lomembase           ; chek and get extended memory
        mov eax,ds:_lomemtop
        sub eax,edi
        cmp eax,48
        mov ds:v86r_dx,offset errmsg1
        jb d_exit16err
        mov ax,500h
        int 31h
        mov eax,es:[edi+14h]
        cmp eax,-1
        jne short d_startf0
        mov eax,(EXTMIN+3) shr 2
d_startf0:
        shl eax,12
        mov edx,eax
        shr eax,10
        cmp eax,EXTMIN
        mov ds:v86r_dx,offset errmsg3
        jb d_exit16err
        or edx,edx
        jz short d_startf1
        mov cx,dx
        shld ebx,edx,16
        mov ax,501h
        int 31h
        mov ds:v86r_dx,offset errmsg5
        jc d_exit16err
        shl ebx,16
        mov bx,cx
        sub ebx,ds:_code32a
        mov ds:_himembase,ebx
        add ebx,edx
        mov ds:_himemtop,ebx
d_startf1:

        mov ax,305h                     ; get save/restore state addxs
        int 31h
        mov ds:dp_savelen,ax
        mov dword ptr ds:dp_saveaddx[0],edi
        mov word ptr ds:dp_saveaddx[4],si
        mov word ptr d_saveaddx[0],cx
        mov word ptr d_saveaddx[2],bx
        mov ax,306h                     ; get switch mode addxs
        int 31h
        mov dword ptr ds:dp_switchaddx[0],edi
        mov word ptr ds:dp_switchaddx[4],si
        mov word ptr d_switchaddx[0],cx
        mov word ptr d_switchaddx[2],bx

        mov ax,400h                     ; set IRQ handlers to PIC values
        int 31h
        xchg dl,dh
        mov bx,dx
        call setintslots

        mov ah,2                        ; backup and set all int vektorz
        mov si,ds:_selcode
        mov edi,19
d_startl0:
        mov bl,ds:intslottbl[edi]
        mov al,4
        int 31h
        lea ebp,[edi*2+edi]
        mov dword ptr ds:dp_ointbuf[ebp*2],edx
        mov word ptr ds:dp_ointbuf[ebp*2+4],cx
        mov al,5
        mov edx,d_nintoff[edi*4]
        mov cx,si
        int 31h
        sub di,1
        jnc d_startl0

        mov ax,es                       ; set up needed regs & go on to 32bit
        mov ss,ax
        add esp,ds:realstackbase
        mov ds,ax
        push dword ptr cs:_selcode
        push offset p_start
        db 66h,0cbh             ; 32bit RETF

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; 16bit VCPI system data
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
v_errmsg0       db      'Incompatible VCPI PIC mappings!!!',7,'$'

v_emmname       db      'EMMXXXX0',0    ; EMS device name
v_emshandle     dw      ?               ; one page allocated to turn on

v_pagedirseg    dw      ?               ; seg of page directory
v_pagebase      dw      0               ; first page of himem (*4)+1000h
v_pagetop       dw      0               ; top page of himem (*4)+1000h

v_ss_cr3        dd      ?               ; new CR3 for pmode (physical)
v_ss_gdtaddxptr dw      c_gdt32addx,0   ; ptr to GDT data for pmode
v_ss_idtaddxptr dw      c_idt32addx,0   ; ptr to IDT data for pmode
v_ss_ldtsel     dw      0               ; don't need no stinkin LDTs
v_ss_trsel      dw      30h             ; task state segment selector
v_ss_dest       dd      ?               ; start in pmode EIP
                dw      20h             ; start in pmode CS
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; 16bit VCPI system code
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
;═════════════════════════════════════════════════════════════════════════════
v_retreal:                              ; VCPI return to real mode
        movzx edi,exitrout
        mov esi,esp
        sub esi,ds:realstackbase
        mov cx,code16
;═════════════════════════════════════════════════════════════════════════════
; EDI=offset to jump to, ESI=real mode stack ptr, CX=real mode DS
v_retreal0:                             ; VCPI go to real mode
        sub esp,8
        push ecx
        push dword ptr ds:v86r_es
        dw 06866h,codeend,0     ; PUSH dword codeend
        push esi
        pushfd
        dw 06866h,code16,0      ; PUSH dword code16
        push edi
        mov ax,gs
        mov ds,ax
        mov ax,0de0ch
        call cs:vp_vcpipmentry
;─────────────────────────────────────────────────────────────────────────────
v_exit2:                                ; VCPI exit (clean up pages)
        mov es,v_pagedirseg
        mov si,v_pagebase
        mov cx,v_pagetop
        sub cx,si
        jz short v_exit
v_exit2l0:
        mov edx,es:[si]
        and dx,0f000h
        mov ax,0de05h
        int 67h
        add si,4
        sub cx,4
        jnz v_exit2l0
;─────────────────────────────────────────────────────────────────────────────
v_exit:                                 ; VCPI exit (clean up EMS page)
        mov ah,45h
        mov dx,v_emshandle
        int 67h
        jmp exit
;─────────────────────────────────────────────────────────────────────────────
v_exiterr1:                             ; VCPI not enough low mem exit
        mov dx,offset errmsg1
        jmp exit16err
;═════════════════════════════════════════════════════════════════════════════
v_start:                                ; start continues from VCPI
        or ds:_sysbyte0,2               ; set system type VCPI byte
        mov ds:code16off,offset v_retreal       ; VCPI return to real mode
        mov c_idt32handler[48h],offset vp_int33 ; VCPI safe int handlers
        mov c_idt32handler[4ch],offset vp_int32
        mov ds:cp_v86irqintr[4],offset vp_int33f0 ; VCPI IRQ safe int routine
        mov int32m0,VINT32              ; VCPI real INT32
        mov exitrout,offset v_exit      ; set EMS cleanup exit

        mov ax,0de0ah                   ; get PIC mappings
        int 67h
        mov bh,cl
        mov dx,offset v_errmsg0         ; chek for compatible PIC mapping
        cmp bl,bh
        je exit16err
        cmp bl,30h
        je exit16err
        cmp bh,30h
        je exit16err
        mov ax,70h                      ; compatible, get highest needed num
        cmp al,bl
        ja short v_startf1
        mov al,bl
v_startf1:
        cmp al,bh
        ja short v_startf2
        mov al,bh
v_startf2:
        add al,7
        mov c_numofintvects,al
        lea eax,[eax*8+7]               ; set limit of IDT
        mov c_idt32addx,ax
        call setintslots                ; set int slots needed
        movzx eax,ax
        add eax,2068h+1
        call pregetlomem                ; allocate TSS, IO bitmap, and IDT
        mov ds:cp_tssesp0ptr,eax
        mov eax,ds:_code16a             ; adjust mode switch structure
        add dword ptr v_ss_gdtaddxptr,eax
        add dword ptr v_ss_idtaddxptr,eax

        mov exitrout,offset v_exit2     ; set VCPI cleanup exit

        mov eax,ds:_lomembase           ; align lomem base on a page
        mov ebx,ds:_code32a
        add ebx,eax
        lea ecx,[ebx+0fffh]
        and ecx,0fffff000h
        sub ebx,ecx
        sub eax,ebx
        mov ds:_lomembase,eax
        mov ebp,ds:_lomemtop            ; get available low memory
        sub ebp,eax
        sub ebp,LOWMIN*1024             ; die if not enough
        jc v_exiterr1
        cmp ebp,8192
        jb v_exiterr1

        shld eax,ecx,28                 ; get segment and clear all pages
        mov v_pagedirseg,ax
        mov es,ax
        xor di,di
        mov cx,2048
        xor eax,eax
        rep stos dword ptr es:[di]
        mov di,1000h                    ; get VCPI pmode interface
        mov esi,offset ds:gdt32vcpi
        mov ax,0de01h
        int 67h
        mov dword ptr ds:vp_vcpipmentry,ebx

        mov v_pagebase,di               ; set up and go through allocation
        mov v_pagetop,di
        movzx eax,di
        sub eax,1000h
        shl eax,10
        mov ebp,ds:_code32a
        sub eax,ebp
        mov ds:_himembase,eax
        mov ebx,8192
v_startl2:
        mov ax,0de04h
        int 67h
        or ah,ah
        jnz short v_startl2d
        test di,0fffh
        jnz short v_startf4
        add ebx,4096
        cmp ebx,ebp
        ja v_exiterr1
v_startf4:
        and dx,0f000h
        or dl,7
        mov es:[di],edx
        add di,4
        jnc v_startl2
v_startl2d:
        mov v_pagetop,di
        lea si,[di-1000h]
        movzx eax,si
        shl eax,10
        sub eax,ebp             ; EBP=_code32a
        mov ds:_himemtop,eax
        sub di,v_pagebase
        cmp di,EXTMIN
        mov dx,offset errmsg3
        jb exit16err
        add ds:_lomembase,ebx

        movzx ebx,v_pagedirseg          ; set up physical addresses
        shr ebx,8
        mov eax,es:[ebx*4+1000h]
        mov v_ss_cr3,eax
        xor di,di
v_startl3:
        inc ebx
        mov eax,es:[ebx*4+1000h]
        and ax,0f000h
        or al,7
        stos dword ptr es:[di]
        sub si,1000h
        ja v_startl3

        mov edi,offset c_startf1        ; offset to jump to in pmode
        mov ebx,ds:cp_tssesp0ptr
        jmp v_switchtopmode             ; duh?

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; 16 bit XMS system data
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
x_calladdx      dd      ?               ; XMS driver addx
x_handle        dw      ?               ; XMS handle of extended memory
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; 16 bit XMS system code
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
;─────────────────────────────────────────────────────────────────────────────
x_exit:                                 ; XMS exit (clean up allocation)
        mov ax,cs
        mov ds,ax
        mov dx,x_handle
        mov ah,0dh
        call x_calladdx
        mov ah,0ah
        call x_calladdx
        jmp exit
;─────────────────────────────────────────────────────────────────────────────
x_exiterr5:                             ; exit with error message 5
        mov dx,offset errmsg5
        jmp exit16err
;═════════════════════════════════════════════════════════════════════════════
x_start:                                ; start in an XMS system
        or ds:_sysbyte0,1               ; set system type XMS byte

        mov ax,4310h                    ; get XMS driver addx
        int 2fh
        mov word ptr x_calladdx[0],bx
        mov word ptr x_calladdx[2],es

        mov ah,3                        ; XMS enable A20
        call x_calladdx
        or ax,ax
        mov dx,offset errmsg4
        jz exit16err

        mov ah,8                        ; chek and get extended memory
        call x_calladdx
        sub ax,64
        jnc short x_startf0
        xor ax,ax
x_startf0:
        cmp ax,EXTMIN
        mov dx,offset errmsg3
        jb exit16err
        mov dx,ax
        movzx ecx,ax
        shl ecx,10
        mov ah,9
        call x_calladdx
        or ax,ax
        jz x_exiterr5
        mov x_handle,dx
        mov exitrout,offset x_exit
        mov ah,0ch
        call x_calladdx
        or ax,ax
        jz x_exiterr5
        shrd eax,edx,16
        mov ax,bx
        sub eax,ds:_code32a
        mov ds:_himembase,eax
        add eax,ecx
        mov ds:_himemtop,eax

        jmp c_startf0                   ; go on to custom start

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; 16 bit custom system data
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
c_idt16addx     dw      3ffh, 0,0       ; default real mode IDT addx&limit
c_idt32addx     dw      3bfh, ?,?       ; 32bit IDT addx&limit
c_gdt32addx     dw      04fh            ; 32bit GDT addx&limit
                dd      offset gdt32    ;

c_numofintvects db      77h             ; number of int vects needed -1
c_idt32handler  dd      offset cp_irq0,offset cp_irq1,offset cp_irq2,offset cp_irq3
                dd      offset cp_irq4,offset cp_irq5
                dd      offset cp_irq6,offset cp_irq7,offset cp_irq8,offset cp_irq9
                dd      offset cp_irqa,offset cp_irqb
                dd      offset cp_irqc,offset cp_irqd,offset cp_irqe,offset cp_irqf
                dd      offset cp_int35,offset cp_int34,offset cp_int33,offset cp_int32,offset cp_int31
                dd      offset cp_exc0,offset cp_exc1,offset cp_exc2,offset cp_exc3
                dd      offset cp_exc4,offset cp_exc5
                dd      offset cp_exc6,offset cp_exc7,offset cp_exc8,offset cp_exc9
                dd      offset cp_exca,offset cp_excb
                dd      offset cp_excc,offset cp_excd,offset cp_exce
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; 16 bit custom system code
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
;═════════════════════════════════════════════════════════════════════════════
c_irqreal:                              ; real mode IRQ
        pushf
        push cs
        push offset icreald2
        jmp icreal
;-----------------------------------------------------------------------------
c_retreal1:                             ; load some real mode stuff & exit
        mov ax,codeend
        mov ss,ax
        mov esp,STAKLEN*10h
        jmp exitrout
;═════════════════════════════════════════════════════════════════════════════
c_retreal0:                             ; load real mode IDT and set PE=0
        mov ax,28h
        mov ds,ax
        mov es,ax
        mov fs,ax
        mov gs,ax
        mov ss,ax
        lidt fword ptr c_idt16addx
        mov eax,cr0
        and al,0feh
        mov cr0,eax
        db 0eah                 ; JMP FAR PTR c_retreal0m0
c_retreal0m0    dw c_sicreal,code16
;-----------------------------------------------------------------------------
c_sicreal:                              ; safe real mode int or call
        mov ax,codeend
        mov ss,ax
        mov ds,cs:v86r_ds
        mov es,cs:v86r_es
        db 0eah                 ; JMP FAR PTR c_sicrealm0
c_sicrealm0     dw      ?,code16;
;═════════════════════════════════════════════════════════════════════════════
c_retreal:                              ; return to real mode
        mov c_retreal0m0,offset c_retreal1
        mov esp,STAKLEN*10h
        jmp c_retreal0
;═════════════════════════════════════════════════════════════════════════════
c_start:                                ; custom only start
        call enableA20                  ; enable that stupid A20 thingy

        mov ah,88h                      ; chek and get extended mem
        int 15h
        cmp ax,EXTMIN
        mov dx,offset errmsg3
        jb exit16err
        movzx eax,ax
        shl eax,10
        mov ebx,100000h
        sub ebx,ds:_code32a
        mov ds:_himembase,ebx
        add eax,ebx
        mov ds:_himemtop,eax
;═════════════════════════════════════════════════════════════════════════════
c_startf0:                              ; start continues from custom or XMS
        mov int32m0,CINT32              ; raw/XMS real INT32
        mov eax,2428h                   ; allocate TSS, IO bitmap, and IDT
        call pregetlomem
        mov ebx,eax

        lgdt fword ptr c_gdt32addx      ; switch to pmode
        mov eax,cr0
        or al,1
        mov cr0,eax
        db 0eah
        dw $+4,20h
;-----------------------------------------------------------------------------
; EBX->TSS
c_startf1:                              ; in 16bit pmode
        mov ax,28h                      ; set up segregs
        mov ds,ax
        mov al,18h
        mov gs,ax
        mov al,10h
        mov es,ax
        mov fs,ax
        mov ss,ax
        mov esp,STAKLEN*16
        add esp,ds:realstackbase

        mov word ptr v_ss_dest[4],8     ; VCPI enter 32bit pmode from now on
        lea eax,[ebx+4]                 ; addx of ESP0 in TSS
        mov ds:cp_tssesp0ptr,eax
        mov ebp,ds:_code32a             ; TSS location in mem to GDT
        lea eax,[ebx+ebp]
        mov ecx,offset ds:gdt32task     ; set up task
        or dword ptr ds:[ecx+2],eax
        mov byte ptr ds:[ecx+5],89h
        mov cx,30h
        ltr cx
        add eax,2068h                   ; set up IDT
        mov ecx,offset c_idt32addx
        mov dword ptr [ecx+2],eax
        lidt fword ptr [ecx]

        mov dword ptr es:[ebx+8],10h    ; set up TSS stuff (EBX->TSS)
        mov edi,104
        mov es:[ebx+102],di
        mov word ptr es:[ebx+100],0
        add edi,ebx                     ; fill IO bitmap with 0
        xor eax,eax
        mov ecx,800h
        rep stos dword ptr es:[edi]

        mov ds:cp_idt32ptr,edi          ; set up blank IDT entries
        movzx esi,c_numofintvects
c_startl0:
        mov dword ptr es:[edi+esi*8],80000h+offset cp_excf
        mov dword ptr es:[edi+esi*8+4],8e00h
        sub si,1
        jnc c_startl0
        mov si,23h                      ; necessary IDT entries
c_startl1:
        movzx ebp,ds:intslottbl[esi]
        mov eax,c_idt32handler[esi*4]
        cmp bp,13
        jne short c_startl1c
        mov ds:cp_int13vect,eax
        mov eax,offset cp_excd
c_startl1c:
        mov es:[edi+ebp*8],ax
        sub si,1
        jnc c_startl1

        mov edi,offset p_start          ; set up regs & go on to 32bit
	mov ax,10h
        mov ds,ax
;-----------------------------------------------------------------------------
c_gotopmode:				; jump to 32bit pmode
        pushfd                          ; set eflags: NT=0, IOPL=3
        pop eax
        and ah,0bfh
        or ah,30h
        push eax
        popfd
        dw 6866h,8,0            ; PUSH dword 8
	push edi
        db 66h,0cbh             ; 32bit RETF

code16  ends

;▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
; 32bit pmode code
;▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
code32  segment para public use32
        assume cs:code32, ds:code32
        org 0

extrn   _main:near

public  _exit, _ret, _getmem, _getlomem, _gethimem, _lomemsize, _himemsize
public  _getirqmask, _setirqmask

public  v86r_eax, v86r_ebx, v86r_ecx, v86r_edx, v86r_esi, v86r_edi, v86r_ebp
public  v86r_ax, v86r_bx, v86r_cx, v86r_dx, v86r_si, v86r_di, v86r_bp
public  v86r_al, v86r_ah, v86r_bl, v86r_bh, v86r_cl, v86r_ch, v86r_dl, v86r_dh
public  v86r_ds, v86r_es, v86r_fs, v86r_gs
public  _selcode, _seldata, _selzero, _lomembase, _lomemtop, _himembase
public  _himemtop, _pspa, _code16a, _code32a, _getirqvect, _setirqvect
public  _sysbyte0, _irqmode
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; 32 bit common system data
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
_lomembase      dd      ?               ; low mem base for allocation
_lomemtop       dd      ?               ; top of low mem
_himembase      dd      0               ; high mem base for allocation
_himemtop       dd      0               ; top of high mem
_pspa           dd      ?               ; offset of start of PSP from 0
_code16a        dd      ?               ; offset of start of 16bit code from 0
_code32a        dd      ?               ; offset of start of 32bit code from 0
_selcode        dw      8               ; code segment selector
_seldata        dw      10h             ; data segment alias for code
_selzero        dw      18h             ; data segment starting at 0:0
_irqmode        dw      0ffffh          ; IRQ mode bits: 0=normal, 1=safe
                db      0ffh            ; misc byte, has to follow _irqmode
_sysbyte0       db      0               ; system bits:
                                        ;  0-1: 0=raw, 1=XMS, 2=VCPI, 3=DPMI

_getirqvect     dd      cp_getirqvect   ; get IRQ handler offset routine addx
_setirqvect     dd      cp_setirqvect   ; set IRQ handler offset routine addx

gdt32           dq      0
gdt32code32     db      0ffh,0ffh,0,0,0,9ah,0cfh,0
gdt32data32     db      0ffh,0ffh,0,0,0,92h,0cfh,0
gdt32zero32     db      0ffh,0ffh,0,0,0,92h,0cfh,0
gdt32code16     db      0ffh,0ffh,0,0,0,9ah,0,0
gdt32data16     db      0ffh,0ffh,0,0,0,92h,0,0
gdt32task       db      0ffh,0ffh,0,0,0,89h,0,0
gdt32vcpi       dq      3 dup(?)

v86r_edi        label   dword           ; vregs for pmode<>real communication
v86r_di         dw      ?, ?            ;  needz to stay this way cuz its a
v86r_esi        label   dword           ;  stupid DPMI structure thingy
v86r_si         dw      ?, ?
v86r_ebp        label   dword
v86r_bp         dw      ?, ?
                dd      0
v86r_ebx        label   dword
v86r_bx         label   word
v86r_bl         db      ?
v86r_bh         db      ?, ?,?
v86r_edx        label   dword
v86r_dx         label   word
v86r_dl         db      ?
v86r_dh         db      ?, ?,?
v86r_ecx        label   dword
v86r_cx         label   word
v86r_cl         db      ?
v86r_ch         db      ?, ?,?
v86r_eax        label   dword
v86r_ax         label   word
v86r_al         db      ?
v86r_ah         db      ?, ?,?
v86r_flags      dw      ?
v86r_es         dw      ?
v86r_ds         dw      ?
v86r_fs         dw      ?
v86r_gs         dw      ?
                dd      0,0

oint1bvect      dd      ?               ; old real int 1bh vektor (ctrl+break)
oint32vect      dd      ?               ; old real int 32h vector
oirqmask        dw      ?               ; old port 21h and 0a1h masks
intslottbl      db      8,9,0ah,0bh,0ch,0dh,0eh,0fh,70h,71h,72h,73h,74h,75h,76h,77h
                db      35h,34h,33h,32h,31h,0,1,2,3,4,5,6,7,8,9,0ah,0bh,0ch,0dh,0eh

code16off       dw      c_retreal       ; offset in 16bit of exit function
code16sel       dw      20h             ; 16bit pmode code selector
data16sel       dw      28h             ; 16bit pmode data selector

nextmodestack   dw      (STAKLEN-STAKSAFE)*16   ; stack for next mode switch
realstackbase   dd      ?               ; linear ptr to beginning of codeend

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; 32 bit common system code
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
;═════════════════════════════════════════════════════════════════════════════
p_cpmode2:				; call pmode from V86
        mov gs,cx
        mov cl,10h
        mov ds,cx
        mov es,cx
        mov fs,cx
	sub nextmodestack,STAKSAFE*16
	push p_cpmodem2
	mov p_cpmodem2,V86CPMODED
        mov eax,[esp+22]
	mov p_cpmodem0,eax
        mov al,[esp+43]
	shr al,1
	and al,1
	add al,0fah
	mov p_cpmodem1,al
        jmp short p_cpmode
;═════════════════════════════════════════════════════════════════════════════
p_cpmode1:                              ; call pmode, load all
        mov esp,ebx
        mov ax,10h
        mov ds,ax
        mov es,ax
        mov ss,ax
;-----------------------------------------------------------------------------
p_cpmode0:                              ; call pmode, load FS and GS
        mov fs,_seldata
        mov gs,_selzero
;-----------------------------------------------------------------------------
p_cpmode:                               ; call pmode routine from real
        push offset p_cpmoded
        cld
        mov eax,v86r_eax
        mov ecx,v86r_ecx
        mov edx,v86r_edx
        mov ebx,v86r_ebx
        mov esi,v86r_esi
        mov edi,v86r_edi
        mov ebp,v86r_ebp
        db 68h                  ; PUSH destination address
p_cpmodem0      dd      ?       ;
p_cpmodem1      db      ?       ; CLI or STI
        ret
;-----------------------------------------------------------------------------
p_cpmoded:                              ; call to pmode done
        cli
        pushf
        pop v86r_flags
        mov v86r_eax,eax
        mov v86r_ecx,ecx
        mov v86r_edx,edx
        mov v86r_ebx,ebx
        mov v86r_esi,esi
        mov v86r_edi,edi
        mov v86r_ebp,ebp
        mov ecx,_code16a
p_cpmodem2        label word            ; return to real, modifiable to JMP
;-----------------------------------------------------------------------------
        movzx ebx,gs:savedstakoff[ecx]  ; DPMI return to real mode
        mov dx,gs:savedstakseg[ecx]
        mov ax,code16
        mov cx,dx
        mov si,ax
        mov edi,offset int32d0
        jmp dp_switchaddx
;-----------------------------------------------------------------------------
VCPMODED=(($-(p_cpmodem2+2))shl 8)+0ebh
p_cpmoded2:                             ; VCPI done with pmode
        movzx esi,gs:savedstakoff[ecx]
        mov cx,code16
        mov edi,offset int32d1
        db 0eah                 ; 16bit JMP FAR 20h:v_retreal0
        dw v_retreal0,0,20h     ;
;-----------------------------------------------------------------------------
CCPMODED=(($-(p_cpmodem2+2))shl 8)+0ebh
p_cpmoded3:                             ; raw/XMS done with pmode
        mov gs:c_retreal0m0[ecx],offset int32d3
        db 0eah                 ; 16bit JMP FAR 20h:c_retreal0
        dw c_retreal0,0,20h     ;
;-----------------------------------------------------------------------------
if ($-(p_cpmodem2+2)) gt 127
  err
endif
V86CPMODED=(($-(p_cpmodem2+2))shl 8)+0ebh
p_cpmoded4:				; V86 done with pmode
	pop p_cpmodem2
        jmp cp_int3_d3
;═════════════════════════════════════════════════════════════════════════════
p_start:                                ; common 32bit start
        mov eax,gs:[1bh*4]              ; neutralize crtl+break
        mov oint1bvect,eax
        db 65h,67h,0c7h,6       ; MOV DWORD PTR GS:[1bh*4],code16:nullint
        dw 1bh*4,nullint,code16 ;
        mov eax,gs:[32h*4]              ; set up for new real mode INT32
        mov oint32vect,eax
        db 65h,67h,0c7h,6       ; MOV DWORD PTR GS:[32h*4],code16:int32
        dw 32h*4,int32,code16   ;
        in al,21h                       ; save old PIC masks
        mov ah,al
        in al,0a1h
        mov oirqmask,ax
        jmp _main                       ; go to main code

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; Allocate any mem, (first cheks low, then high)
; In:
;   EAX - size requested
; Out:
;   CF=0 - memory allocated
;   CF=1 - not enough mem
;   EAX - linear pointer to mem or ?
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
_getmem:
        push eax
        call _getlomem
        jnc short getmemd
        pop eax
        jmp short _gethimem
getmemd:
        add esp,4
_ret:                                   ; generic RET instruction
        ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; Allocate some low mem
; In:
;   EAX - size requested
; Out:
;   CF=0 - memory allocated
;   CF=1 - not enough mem
;   EAX - linear pointer to mem or ?
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
_getlomem:
        add eax,_lomembase
        cmp eax,_lomemtop
        ja short getmemerr
        xchg eax,_lomembase
        clc
        ret
getmemerr:
        stc
        ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; Allocate some high mem
; In:
;   EAX - size requested
; Out:
;   CF=0 - memory allocated
;   CF=1 - not enough mem
;   EAX - linear pointer to mem or ?
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
_gethimem:
        add eax,_himembase
        cmp eax,_himemtop
        ja short getmemerr
        xchg eax,_himembase
        clc
        ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; Get amount of free low mem
; Out:
;   EAX - number of bytes free
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
_lomemsize:
        mov eax,_lomemtop
        sub eax,_lomembase
        ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; Get amount of free high mem
; Out:
;   EAX - number of bytes free
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
_himemsize:
        mov eax,_himemtop
        sub eax,_himembase
        ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; Get status of IRQ mask bit
; In:
;   BL - IRQ num (0-15)
; Out:
;   AL - status: 0=enabled, 1=disabled
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
_getirqmask:
        push ax
        in al,0a1h
        mov ah,al
        in al,21h
        xchg cl,bl
        shr ax,cl
        xchg cl,bl
        and al,1
        mov [esp],al
        pop ax
        ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; Set status of IRQ mask bit
; In:
;   BL - IRQ num (0-15)
;   AL - status: 0=enabled, 1=disabled
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
_setirqmask:
        push ax bx cx dx
        mov cl,bl
        mov bx,0fffeh
        movzx dx,al
        rol bx,cl
        shl dx,cl
        in al,0a1h
        mov ah,al
        in al,21h
        and ax,bx
        or ax,dx
        out 21h,al
        mov al,ah
        out 0a1h,al
        pop dx cx bx ax
        ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; Exit to real mode
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
_exit:
        cli
        mov eax,oint1bvect              ; restore ctrl+break
        mov gs:[1bh*4],eax
        mov eax,oint32vect              ; restore real mode int 32h vector
        mov gs:[32h*4],eax
        mov ax,oirqmask                 ; restore PIC masks
        out 0a1h,al
        mov al,ah
        out 21h,al
        push code16sel                  ; go to 16bit pmode exit code
        push code16off
        mov ds,data16sel
        db 66h,0cbh             ; 16bit RETF

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; 32 bit DPMI system data
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
dp_switchaddx   df      ?               ; switch to real mode addx
dp_saveaddx     df      ?               ; save/restore state addx
dp_savelen      dw      0,0             ; length of state buffer
dp_savedstaksel dw      ?               ; current saved stack selector

dp_ointbuf      df      20 dup(?)       ; saved interrupt addx buffer
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; 32 bit DPMI system code
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
;═════════════════════════════════════════════════════════════════════════════
dp_int32:                               ; DPMI INT32/34: CX:DX=seg:off
        pushad
        shl ecx,16
        mov cx,dx
        mov bp,offset callreal
        mov dl,1
        jmp short dp_int3_
;═════════════════════════════════════════════════════════════════════════════
dp_int33:                               ; DPMI INT33/35: AL=int num
        pushad
        movzx eax,al
        mov ecx,gs:[eax*4]
        mov bp,offset intreal
        xor dl,dl
;-----------------------------------------------------------------------------
dp_int3_:                               ; DPMI int or call to real mode
        mov ax,900h
        int 31h
        push ax
        and al,dl
        add al,0fah
        mov ebx,_code16a
        mov gs:icrealm0[ebx],al
        mov gs:icrealm1[ebx],ecx
        push cp_savedstakoff
        push dp_savedstaksel
        movzx ebx,nextmodestack
        lea eax,[ebx-STAKSAFE*16]
        mov nextmodestack,ax
        mov ax,ss
        mov es,ax
        sub esp,dword ptr dp_savelen
        mov edi,esp
        xor al,al
        call dp_saveaddx
        mov cp_savedstakoff,esp
        mov dp_savedstaksel,ss
        mov dx,codeend
        mov ax,v86r_ds
        mov cx,v86r_es
        movzx edi,bp
        mov si,code16
        jmp dp_switchaddx
;-----------------------------------------------------------------------------
dp_int3_d:                              ; done with real mode int or call
        mov edi,esp
        mov al,1
        call dp_saveaddx
        add esp,dword ptr dp_savelen
        pop dp_savedstaksel
        pop cp_savedstakoff
        add nextmodestack,STAKSAFE*16
        mov bx,v86r_flags
        pop ax
        int 31h
        mov ax,ds
        mov es,ax
        mov fs,ax
        mov gs,_selzero
        jmp cp_int3_d2
;═════════════════════════════════════════════════════════════════════════════
; DPMI IRQ redirectors (needed to make all IRQ vector selectors = CS)
dp_irq0:
        jmp cs:dp_ointbuf[0]
dp_irq1:
        jmp cs:dp_ointbuf[6]
dp_irq2:
        jmp cs:dp_ointbuf[12]
dp_irq3:
        jmp cs:dp_ointbuf[18]
dp_irq4:
        jmp cs:dp_ointbuf[24]
dp_irq5:
        jmp cs:dp_ointbuf[30]
dp_irq6:
        jmp cs:dp_ointbuf[36]
dp_irq7:
        jmp cs:dp_ointbuf[42]
dp_irq8:
        jmp cs:dp_ointbuf[48]
dp_irq9:
        jmp cs:dp_ointbuf[54]
dp_irqa:
        jmp cs:dp_ointbuf[60]
dp_irqb:
        jmp cs:dp_ointbuf[66]
dp_irqc:
        jmp cs:dp_ointbuf[72]
dp_irqd:
        jmp cs:dp_ointbuf[78]
dp_irqe:
        jmp cs:dp_ointbuf[84]
dp_irqf:
        jmp cs:dp_ointbuf[90]

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; DPMI get IRQ handler offset
; In:
;   BL - IRQ num (0-0fh)
; Out:
;   EDX - offset of IRQ handler
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
dp_getirqvect:
        push ax ebx cx
        movzx ebx,bl
        mov bl,intslottbl[ebx]
        mov ax,204h
        int 31h
        pop cx ebx ax
        ret
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; DPMI set IRQ handler offset
; In:
;   BL - IRQ num (0-0fh)
;   EDX - offset of IRQ handler
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
dp_setirqvect:
        push ax ebx cx
        movzx ebx,bl
        mov bl,intslottbl[ebx]
        mov cx,cs
        mov ax,205h
        int 31h
        pop cx ebx ax
        ret

;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; 32 bit custom/XMS/VCPI system data
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
vp_vcpipmentry  df      3800000000h     ; VCPI entry point in pmode

cp_tssesp0ptr   dd      0               ; ptr to ESP0 in TSS, or null in VCPI
cp_idt32ptr     dd      ?               ; ptr to 32bit IDT
cp_int13vect    dd      0               ; interrupt vektor 13 ptr
cp_validirqesp  dd      0               ; valid IRQ ESP value for exc 13

cp_v86irqintr   dd      cp_int35f1,cp_int33f0   ; IRQ int call routines
cp_v86irqnum    db      ?               ; IRQ num for V86 mode
cp_v86irqmode   db      ?               ; IRQ mode for V86 mode (safe/norm)
cp_savedstakoff dd      ?               ; current saved stack offset
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
; 32 bit custom/XMS/VCPI system code
;▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
;═════════════════════════════════════════════════════════════════════════════
cp_int31:                               ; INT 31h: AX=900h,901h,902h
        cmp al,1
        mov al,[esp+9]
        jb short cp_int31f0
        ja short cp_int31f1
        or byte ptr [esp+9],2
        jmp short cp_int31f1
cp_int31f0:
        and byte ptr [esp+9],0fdh
cp_int31f1:
        shr al,1
        and al,1
        iretd
;═════════════════════════════════════════════════════════════════════════════
vp_int32:                               ; VCPI INT 32h: safe CX:DX=seg:off
        pushad
        mov ebp,offset callreal
        mov si,VICREAL1D
        mov bl,2
        jmp short cp_int34f0
;═════════════════════════════════════════════════════════════════════════════
vp_int33:                               ; VCPI INT 33h: safe AL=int num
        pushad
        mov ebp,offset intreal
;-----------------------------------------------------------------------------
vp_int33f0:
        mov si,VICREAL1D
        mov bl,2
        jmp short cp_int35f0
;═════════════════════════════════════════════════════════════════════════════
cp_int32:                               ; INT 32h: safe CX:DX=seg:off
        pushad
        mov ebp,offset callreal
        mov si,CICREAL1D
        mov bl,1
        jmp short cp_int34f0
;═════════════════════════════════════════════════════════════════════════════
cp_int33:                               ; INT 33h: safe AL=int num
        pushad
        mov ebp,offset intreal
;-----------------------------------------------------------------------------
cp_int33f0:
        mov si,CICREAL1D
        mov bl,1
        jmp short cp_int35f0
;═════════════════════════════════════════════════════════════════════════════
cp_int34:                               ; INT 34h: normal CX:DX=seg:off
        pushad
        mov ebp,offset callreal
        mov si,CICREAL0D
        xor bl,bl
;-----------------------------------------------------------------------------
cp_int34f0:
        shl ecx,16
        mov cx,dx
        mov bh,1
        jmp short cp_int3_
;═════════════════════════════════════════════════════════════════════════════
cp_int35:                               ; INT 35h: normal AL=int num
        pushad
        mov ebp,offset intreal
;-----------------------------------------------------------------------------
cp_int35f1:
        mov si,CICREAL0D
        xor bl,bl
;-----------------------------------------------------------------------------
cp_int35f0:
        movzx eax,al
        mov ecx,gs:[eax*4]
        xor bh,bh
;-----------------------------------------------------------------------------
cp_int3_:                               ; int or call to real mode
        mov edi,[esp+40]
        shld eax,edi,23
        and al,bh
        add al,0fah
        mov edx,_code16a
        mov gs:icrealm0[edx],al
        mov gs:icrealm1[edx],ecx
        xchg gs:icrealm2[edx],si
        push si
        movzx esi,nextmodestack
        lea eax,[esi-STAKSAFE*16]
        mov nextmodestack,ax
        add eax,realstackbase
        mov edx,cp_tssesp0ptr
        push dword ptr [edx]
        mov [edx],eax
        sub eax,36
        push cp_validirqesp
        mov cp_validirqesp,eax
        push cp_savedstakoff
        mov cp_savedstakoff,esp
        cmp bl,1
        jb short cp_int3_n
        ja short vp_int3_s
;-----------------------------------------------------------------------------
cp_int3_s:                              ; safe real mode int or call
        mov edx,_code16a
        mov gs:c_sicrealm0[edx],bp
        mov esp,esi
        db 0eah                 ; 16bit JMP FAR 20h:c_retreal0
        dw c_retreal0,0,20h     ;
;-----------------------------------------------------------------------------
vp_int3_s:                              ; safe VCPI real mode int or call
        mov edi,ebp
        mov cx,v86r_ds
        mov ax,28h
        mov ds,ax
        db 0eah                 ; 16bit JMP FAR 20h:v_retreal0
        dw v_retreal0,0,20h     ;
;-----------------------------------------------------------------------------
cp_int3_n:                              ; normal real mode int or call
        sub esp,8
        push dword ptr v86r_ds
        push dword ptr v86r_es
        db 68h                  ; 32bit PUSH codeend
        dd codeend              ;
        push esi
        or edi,20000h
        and di,0fdffh
        push edi
        db 68h                  ; 32bit PUSH code16
        dd code16               ;
        push ebp
        iretd
;-----------------------------------------------------------------------------
cp_int3_d:                              ; done with real mode int or call
        mov ax,18h
        mov gs,ax
        mov ax,10h
        mov ds,ax
        mov es,ax
        mov fs,ax
        mov ss,ax
        mov esp,cp_savedstakoff
        pop cp_savedstakoff
        pop cp_validirqesp
        mov ebx,cp_tssesp0ptr
        pop dword ptr [ebx]
        mov ebx,_code16a
        pop gs:icrealm2[ebx]
;-----------------------------------------------------------------------------
cp_int3_d3:                             ; done from real mode pmode call
        add nextmodestack,STAKSAFE*16
        mov bx,v86r_flags
;-----------------------------------------------------------------------------
cp_int3_d2:				; done from DPMI also
        mov ax,[esp+40]
        and ax,not 8d5h
        and bx,8d5h
        or ax,bx
        mov [esp+40],ax
        popad
        iretd
;═════════════════════════════════════════════════════════════════════════════
cp_excd:                                ; general protection violation
        cmp esp,cs:cp_validirqesp       ; source an IRQ or exception¿
        jne short cp_excdf0
        jmp cs:cp_int13vect
cp_excdf0:
        test byte ptr [esp+14],2        ; exception, from V86?
        jnz short cp_excdv86
        pushad                          ; nope, pmode exception
        mov al,0dh
        jmp cp_exc
;-----------------------------------------------------------------------------
cp_excdv86:                             ; violation from V86 mode
        add esp,4
        pushad
        mov cx,18h
        mov ds,cx
        movzx ebx,word ptr [esp+36]
        shl ebx,4
        add ebx,[esp+32]
        inc word ptr [esp+32]
        mov al,[ebx]
        mov edx,3
        cmp al,0cch
        je short cp_v86int
        mov dl,4
        cmp al,0ceh
        je short cp_v86int
        inc word ptr [esp+32]
        mov dl,[ebx+1]
        cmp dl,32h
        je p_cpmode2
        cmp dl,0ffh
        je cp_int3_d
;-----------------------------------------------------------------------------
cp_v86int:                              ; need to simulate a real mode int
        movzx ebx,word ptr [esp+48]
        shl ebx,4
        sub word ptr [esp+44],6
        add ebx,[esp+44]
        mov ax,[esp+40]
        mov [ebx+4],ax
        and ah,0fch
        mov [esp+41],ah
        mov ax,[esp+36]
        mov [ebx+2],ax
        mov ax,[esp+32]
        mov [ebx],ax
        mov eax,[edx*4]
        mov [esp+32],ax
        shr eax,16
        mov [esp+36],ax
        popad
        iretd
;═════════════════════════════════════════════════════════════════════════════
; all exceptions except 0dh. all are terminal, others are redirected.
cp_exc0:
        push eax
        mov ax,1000h
        jmp cp_irq
cp_exc1:
        push eax
        mov ax,1001h
        jmp cp_irq
cp_exc2:
        push eax
        mov ax,1002h
        jmp cp_irq
cp_exc3:
        pushad
        mov al,3
        jmp short cp_exc
cp_exc4:
        pushad
        mov al,4
        jmp short cp_exc
cp_exc5:
        push eax
        mov ax,1005h
        jmp cp_irq
cp_exc6:
        pushad
        mov al,6
        jmp short cp_exc
cp_exc7:
        push eax
        mov ax,1007h
        jmp cp_irq
cp_exc8:
        pushad
        mov al,8
        jmp short cp_exc
cp_exc9:
        pushad
        mov al,9
        jmp short cp_exc
cp_exca:
        pushad
        mov al,0ah
        jmp short cp_exc
cp_excb:
        pushad
        mov al,0bh
        jmp short cp_exc
cp_excc:
        pushad
        mov al,0ch
        jmp short cp_exc
cp_exce:
        pushad
        mov al,0eh
        jmp short cp_exc
cp_excf:
        pushad
        mov al,0ffh
;-----------------------------------------------------------------------------
cp_exc:                                 ; main exception handler
        mov ax,10h
        mov ds,ax
        mov es,ax
        mov fs,ax
        mov gs,_selzero
        cld
        jmp _exit
;═════════════════════════════════════════════════════════════════════════════
; IRQ redirector between modes
cp_irq0:
        push eax
        mov ax,0008h
        jmp short cp_irq
cp_irq1:
        push eax
        mov ax,0109h
        jmp short cp_irq
cp_irq2:
        push eax
        mov ax,020ah
        jmp short cp_irq
cp_irq3:
        push eax
        mov ax,030bh
        jmp short cp_irq
cp_irq4:
        push eax
        mov ax,040ch
        jmp short cp_irq
cp_irq5:
        push eax
        mov ax,050dh
        jmp short cp_irq
cp_irq6:
        push eax
        mov ax,060eh
        jmp short cp_irq
cp_irq7:
        push eax
        mov ax,070fh
        jmp short cp_irq
cp_irq8:
        push eax
        mov ax,0870h
        jmp short cp_irq
cp_irq9:
        push eax
        mov ax,0971h
        jmp short cp_irq
cp_irqa:
        push eax
        mov ax,0a72h
        jmp short cp_irq
cp_irqb:
        push eax
        mov ax,0b73h
        jmp short cp_irq
cp_irqc:
        push eax
        mov ax,0c74h
        jmp short cp_irq
cp_irqd:
        push eax
        mov ax,0d75h
        jmp short cp_irq
cp_irqe:
        push eax
        mov ax,0e76h
        jmp short cp_irq
cp_irqf:
        push eax
        mov ax,0f77h
;─────────────────────────────────────────────────────────────────────────────
cp_irq:                                 ; main IRQ handler thingy
        mov ss:cp_v86irqnum,al
        movzx eax,ah
        bt dword ptr ss:_irqmode,eax
        setc ss:cp_v86irqmode
        pop eax
        test byte ptr [esp+10],2
        jnz short cp_irqv86
        push ds es fs gs                ; real mode IRQ from pmode
        pushfd
        push cs
        push offset cp_irqpd
        pushad
        mov ax,10h
        mov ds,ax
        mov al,18h
        mov gs,ax
        mov al,cp_v86irqnum
        mov ebp,offset c_irqreal
        movzx ebx,cp_v86irqmode
        jmp cp_v86irqintr[ebx*4]
cp_irqpd:
        pop gs fs es ds
        iretd
;─────────────────────────────────────────────────────────────────────────────
cp_irqv86:                              ; IRQ from V86, safe or norm redirect
        cmp cs:cp_v86irqmode,0
        jne short cp_irqv86s
        pushad                          ; normal IRQ redirection
        mov ax,18h
        mov ds,ax
        movzx edx,ss:cp_v86irqnum
        jmp cp_v86int
;-----------------------------------------------------------------------------
cp_irqv86s:                             ; safe IRQ redirection
        pushfd
        push cs
        push offset cp_irqv86sd
        pushad
        mov ax,10h
        mov ds,ax
        mov al,18h
        mov gs,ax
        sub nextmodestack,STAKSAFE*16
        mov al,cp_v86irqnum
        mov ebp,offset c_irqreal
        jmp cp_v86irqintr[4]
;-----------------------------------------------------------------------------
cp_irqv86sd:                            ; done with safe IRQ
        add nextmodestack,STAKSAFE*16
        iretd

;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; Custom get IRQ handler offset
; In:
;   BL - IRQ num (0-0fh)
; Out:
;   EDX - offset of IRQ handler
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
cp_getirqvect:
        push ebx
        pushf
        cli
        movzx ebx,bl
        mov bl,intslottbl[ebx]
        cmp bl,13
        je short cp_getirqvectf0
        lea ebx,[ebx*8]
        add ebx,cp_idt32ptr
        mov dx,[ebx+6]
        shl edx,16
        mov dx,[ebx]
cp_getirqvectd:
        popf
        pop ebx
        ret
cp_getirqvectf0:
        mov edx,cp_int13vect
        jmp short cp_getirqvectd
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
; Custom set IRQ handler offset
; In:
;   BL - IRQ num (0-0fh)
;   EDX - offset of IRQ handler
;░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
cp_setirqvect:
        push ebx
        pushf
        cli
        movzx ebx,bl
        mov bl,intslottbl[ebx]
        cmp bl,13
        je short cp_setirqvectf0
        lea ebx,[ebx*8]
        add ebx,cp_idt32ptr
        mov [ebx],dx
        shr edx,16
        mov [ebx+6],dx
cp_setirqvectd:
        popf
        pop ebx
        ret
cp_setirqvectf0:
        mov cp_int13vect,edx
        jmp short cp_setirqvectd

code32  ends

;▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
; End of program (must be at end of program or you will suffer)
;▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
codeend segment para stack use32 'stack'
db STAKLEN*16 dup(?)
codeend ends
        end     start16

[ RETURN TO DIRECTORY ]