        format  MZ
        heap    0
        entry   Main:Start
        stack   Stck:EndStack

MinExtMem       = 3072k ; minimum extended memory in KB
;
Debugger        = 1     ;=0
;
X       equ     (wrt Main+100h)
;
DosBufferSize   = 4k
;
segment IniCode use16
        org     100h

macro   int3
{
if Debugger
        int3
end if
}

Init:   mov     ax,7202h
        push    ax
        popf
        pushf
        pop     bx
        cmp     ax,bx
        je      ProcessorOk
        call    InitError
        db      'needed 386+',36
      ProcessorOk:
        smsw    ax
        test    al,1
        jz      ModeOk
        call    InitError
        db      'processor is in protected mode',36
      ModeOk:
        push    0
        pop     ds
        push    _BIOS_
        pop     es
        mov     cx,1k/2
        xor     di,di
        xor     si,si
        cli
        rep     movsw
        mov     ds,cs
        mov     eax,Main
        shl     eax,4
        mov     [ProgramBase+X],eax
        mov     eax,dword 0
        orgstr  $-4
        dw      NewInt22h,IniCode-10h
        cli
        xchg    eax,[000Ah]
        mov     [OldInt22h],eax
        sti

        mov     ax,4300h        ; Check for XMS
        int     2Fh
        cmp     al,80h          ; XMS present?
        je      InitXMS
        mov     ax,$E801        ; Get memory size
        int     15h
        jc      OldCheck
        movzx   edx,dx          ; EDX=Configured mem > 16M in 64KB
        shl     edx,6           ; EDX=Configured mem > 16M in KB
        movzx   eax,cx          ; EAX=Configured mem 1-16M in KB
        add     eax,edx         ; EAX=Total extended memory in KB
        jmp     MemoryOk

OldCheck:
        mov     ah,88h
        int     15h
        movzx   eax,ax
MemoryOk:
        shl     eax,10          ; EAX=Total extended memory in KB
        add     eax,1M
        sub     eax,[ProgramBase+X]
        mov     [HiHeapOrg+X],eax
        mov     [HiHeapEnd+X],eax
        push    ds
        push    0               ; DS := 0
        pop     ds
        call    EnableA20       ; Enable A20
        jz      A20Ok           ; Is A20 enabled?
        pop     ds
NoA20:  call    InitError
        db      'A20 failure',36
A20Ok:  lds     bx,[4*19h]
        mov     eax,1M                  ; Initial free extended memory base
        cmp     dword [bx+12h],'VDIS'   ; VDISK memory allocation?
        jne     NoVDISK                 ; If present, get base of free memory
        mov     eax,[bx+2Ch]            ; Get first free extended memory byte
        add     eax,0Fh                 ; Align on paragraph
        and     eax,$FFFFF0             ; Address is only 24bit
NoVDISK:push    $FFFF                   ; DS := FFFFh for ext mem addressing
        pop     ds
        cmp     dword [13h],'VDIS'      ; VDISK memory allocation?
        jne     VDISKok                 ; If present, get base of free memory
        movzx   ebx,word [2Eh]          ; Get first free kilobyte
        shl     ebx,10
        cmp     eax,ebx                 ; Pick larger of 2 addresses
        ja      VDISKok
        mov     eax,ebx
VDISKok:pop     ds
        sub     eax,[ProgramBase+X]
        mov     [HiHeapOrg+X],eax
        jmp     IsExtendedMemory
InitXMS:mov     ax,4310h        ; Get XMS driver address
        int     2Fh
        push    es bx
        pop     [PtrXMS]
        push    cs
        pop     es

        mov     ah,3            ; Enable A20
        call    GoToXMS
        dec     ax              ; Error enabling A20?
        jnz     NoA20

        mov     ah,0            ; Get XMS version number
        call    GoToXMS
        cmp     ah,3
        setbe   al
        ror     al,1
        mov     [SXMSbit],al    ; Call with bit 7 set in al -> SXMS

        xor     eax,eax         ; Get free extended memory size
        mov     ah,8
        or      ah,[SXMSbit]
        call    GoToXMS
        or      bl,bl
        jnz     NoExtendedMemory
        mov     edx,eax
        shl     eax,10
        mov     [HiHeapEnd+X],eax
        mov     ah,9            ; Allocate largest memory block
        or      ah,0
label SXMSbit byte at $-1
        call    GoToXMS
        dec     ax
        jne     NoExtendedMemory
        mov     [HandleXMS],dx
        mov     ah,0Ch
        call    GoToXMS         ; Lock extended memory block
        dec     ax
        jne     NoExtendedMemory
        shl     edx,16
        mov     dx,bx
        sub     edx,[ProgramBase+X]
        mov     [HiHeapOrg+X],edx
        add     [HiHeapEnd+X],edx
IsExtendedMemory:
        mov     eax,[HiHeapEnd+X]
        sub     eax,[HiHeapOrg+X]
        sub     eax,MinExtMem
        jnc     InitISR
NoExtendedMemory:
        call    InitError
        db      'out of extended memory',36
InitISR:mov     eax,cs
        mov     ds,ax
        shl     eax,4
        add     [GDT],eax
        mov     ax,350Ch
        int     21h
        push    es bx
        pop     [IRQ4]
        mov     ax,250Ch
        mov     dx,NewInt0Ch
        int     21h
        mov     ax,350Dh
        int     21h
        push    es bx
        pop     [IRQ5]
        mov     ax,250Dh
        mov     dx,NewInt0Dh
        int     21h

        mov     ax,2506h
        mov     dx,UndefinedO
        int     21h
if ~Debugger
        mov     ax,2501h
        mov     dx,SingleIRET
        int     21h
        mov     ax,2503h
        mov     dx,SingleIRET
        int     21h
end if
        retf

InitError:
        push    cs
        pop     ds
        mov     dx,InitErrorPrefix
        mov     ah,9
        int     21h
        pop     dx
        int     21h
        mov     dx,InitErrorSuffix
        int     21h
        mov     ax,4CFFh
        int     21h

EnableA20:
        call    TestA20         ; Is A20 already enabled?
        jz      A20Enabled      ; If yes, done
        in      al,92h          ; PS/2 A20 enable
        or      al,2
        out     92h,al
        call    TestA20         ; Is A20 enabled?
        jz      A20Enabled      ; If yes, done
        call    WaitNoData?     ; AT A20 enable
        jnz     TestA20
        mov     al,$D1
        out     64h,al
        call    WaitNoData?
        jnz     TestA20
        mov     al,$DF
        out     60h,al
        call    WaitNoData?
TestA20:push    es              ; Test for enabled A20
        mov     ax,-1
        mov     es,ax
        mov     al,[0]          ; Get byte from 0:0
        mov     ah,al           ; Preserve old byte
        not     al              ; Modify byte
        xchg    al,[es:10h]     ; Put modified byte to FFFFh:10h
        cmp     ah,[0]          ; Set zero if byte at 0:0 not modified
        mov     [es:10h],al     ; Restore byte at FFFFh:10h
        pop     es
A20Enabled:
        ret                     ; Return, zero if A20 enabled

; Wait Not Data in port 60h\64h
WaitNoData?:
        xor     cx,cx
.WaitRetry:
        in      al,64h
        out     $EB,al
        test    al,00000010b
        jz      .Ok
        loop    .WaitRetry
        inc     cx
.Ok:    ret

GoToXMS:call    0:0             ; XMS driver pointer
label PtrXMS dword at $-4
        ret

GDTR:   dw EndGDT-GDT-1         ;Limit of GDT
GDT:    dd GDT                  ;Linear address of GDT
        dd 0                    ;Null descriptor
        dw $FFFF,0,9200h,$CF    ;4GB data descriptor
        dw $FFFF,0,9200h,$00    ;16B data descriptor
EndGDT:

InitErrorPrefix db 'error: ',36
InitErrorSuffix db 13,10,36

NewInt0Ch:
        push    eax
        mov     al,00001011b
        out     20h,al
        in      al,20h
        test    al,00010000b    ;Is IRQ 4 in service?
        jz      Except
        pop     eax
        jmp     0:0
label IRQ4 dword at $-4
Except: push    bx
        cli                     ;Disable interrupts
        mov     bx,ss
        lgdt    [cs:GDTR]       ;Load GDT register
        mov     eax,cr0         ;Switch to protected mode
        or      al,1
        mov     cr0,eax
        jmp     $+2
        mov     ax,1 shl 3      ;Load 32-bit data descriptor
        mov     ss,ax
        mov     eax,cr0         ;Switch back to real mode
        and     al,not 1
        mov     cr0,eax
        jmp     $+2
        mov     ss,bx
        sti                     ;Enable interrupts
        pop     bx eax
SingleIRET:
        iret

UndefinedO:
        mov     cx,cs
        mov     ds,cx
        mov     bx,ExceptionStr+11
        mov     word [bx],'UD'
        push    eax
Protection:
        mov     cx,cs
        mov     ds,cx
        mov     bx,ExceptionStr+17
times 2 pop     eax

        mov     cl,8
_ConvertNumber_:
        rol     eax,4
        push    eax
        and     al,0Fh
        cmp     al,10
        sbb     al,69h
        das
        cmp     byte [bx],':'
        jne     .1
        inc     bx
.1:     mov     [bx],al
        inc     bx
        pop     eax
        dec     cl
        jnz     _ConvertNumber_

        mov     ax,3
        int     10h

        mov     ah,9
        mov     dx,ExceptionStr
        int     21h

        mov     ax,4CFFh
        int     21h

ExceptionStr    db 'Exception #GP at ####:####',13,10,36

NewInt0Dh:
        push    eax
        mov     al,00001011b
        out     20h,al
        in      al,20h
        test    al,00100000b    ;Is IRQ 5 in service?
        jz      Exception
        pop     eax
        jmp     0:0
label IRQ5 dword at $-4
Exception:
        push    ds es fs gs
        cli                     ;Disable interrupts
        lgdt    [cs:GDTR]       ;Load GDT register
        mov     eax,cr0         ;Switch to protected mode
        or      al,1
        mov     cr0,eax
        jmp     StartPM
StartPM:mov     ax,1 shl 3      ;Load 32-bit data descriptor
        mov     ds,ax           ;To all data segment registers
        mov     es,ax
        mov     fs,ax
        mov     gs,ax
        mov     eax,cr0         ;Switch back to real mode
        and     al,not 1
        mov     cr0,eax
        jmp     EndPM
EndPM:  sti                     ;Enable interrupts
        pop     gs fs es ds eax
        iret

NewInt22h:
        push    ds

        push    _BIOS_
        pop     ds
        push    0
        pop     es
        mov     cx,1k/2
        xor     di,di
        xor     si,si
        cld
        cli
        rep     movsw

        pop     ds

        mov     dx,word 0
label HandleXMS word at $-2             ; Handle of XMS memory block
        or      dx,dx
        je      .Exit
        mov     ah,0Dh                  ; Unlock extended memory block
        call    GoToXMS
        mov     ah,0Ah                  ; Free extended memory block
        call    GoToXMS
.Exit:
if 0
        cli                             ; Disable interrupts
        mov     bx,ss
        lgdt    [cs:GDTR]               ; Load GDT register
        mov     eax,cr0                 ; Switch to protected mode
        or      al,1
        mov     cr0,eax
        jmp     $+2
        mov     ax,2 shl 3              ; Load 16-bit data descriptor
        mov     ss,ax
        movzx   esp,sp
        mov     eax,cr0                 ; Switch back to real mode
        and     al,not 1
        mov     cr0,eax
        jmp     $+2
        mov     ss,bx
        sti                             ; Enable interrupts
end if
        jmp     0:0
label OldInt22h dword at $-4

if Debugger & 0
segment Debug
DebugInit:      file "debug16.bin"
end if

segment Main

HiHeapOrg       dd      0
HiHeapEnd       dd      0
ProgramBase     dd      0

TheExe          db 'unpacked.exe',0
TheCom          db 'unpacked.com',0
Stage1st        db 'stage.1st',0
Stage2nd        db 'stage.2nd',0

Zeroes          db 16 dup 0

Start:  call    IniCode-10h:Init

        mov     ax,cs
        mov     ds,ax
        mov     es,ax
if Debugger & 0
        call    Debug:DebugInit
end if
        mov     eax,TBuffer
        shl     eax,4
        sub     eax,[ProgramBase]
        mov     [DosBuffer],eax

        mov     edx,Stage1st
        call    Open
        jc      ErrLoadStage1
        mov     al,2
        call    Seek0
        cmp     eax,1M+5*2
        ja      ErrLoadStage1
        mov     [Size1st],eax
        xor     al,al
        call    Seek0
        mov     ecx,5*2
        mov     edx,ThePSP1
        call    Read
        ;
        mov     eax,[edx+2]
        rol     eax,16
        mov     [edx+2],eax
        ;
        mov     eax,[edx+6]
        rol     eax,16
        mov     [edx+6],eax
        ;
        mov     ecx,[Size1st]
        mov     edx,[HiHeapOrg]
        call    Read
        call    Close
        ;
        mov     edx,Stage2nd
        call    Open
        jc      ErrLoadStage2
        mov     al,2
        call    Seek0
        cmp     eax,1M+5*2
        ja      ErrLoadStage2
        mov     [Size2nd],eax
        xor     al,al
        call    Seek0
        mov     ecx,5*2
        mov     edx,ThePSP2
        call    Read
        ;
        mov     eax,[edx+2]
        rol     eax,16
        mov     [edx+2],eax
        ;
        mov     eax,[edx+6]
        rol     eax,16
        mov     [edx+6],eax
        ;
        mov     ecx,[Size2nd]
        mov     edx,[HiHeapOrg]
        add     edx,1M
        call    Read
        call    Close
        ;
        mov     eax,[Size1st]
        mov     edx,[Size2nd]
        cmp     eax,edx
        jbe     @F
        mov     eax,edx
        ;
      @@:
        ;
        mov     [MinSize],eax
        ;
if 0
        call    GetParams
        ;
        movzx   esi,[0*2+CurZ]
        call    GetValue
        mov     [TheCS1+0],ax
        movzx   esi,[1*2+CurZ]
        call    GetValue
        mov     [TheCS1+2],ax
        movzx   esi,[2*2+CurZ]
        call    GetValue
        mov     [TheSS1+0],ax
        movzx   esi,[3*2+CurZ]
        call    GetValue
        mov     [TheSS1+2],ax
        movzx   esi,[4*2+CurZ]
        call    GetValue
        mov     [ThePSP1],eax
        ;
        movzx   esi,[6*2+CurZ]
        call    GetValue
        mov     [TheCS2+0],ax
        movzx   esi,[7*2+CurZ]
        call    GetValue
        mov     [TheCS2+2],ax
        movzx   esi,[8*2+CurZ]
        call    GetValue
        mov     [TheSS2+0],ax
        movzx   esi,[9*2+CurZ]
        call    GetValue
        mov     [TheSS2+2],ax
        movzx   esi,[10*2+CurZ]
        call    GetValue
        mov     [ThePSP2],eax
        ;
        mov     eax,07200720h
        mov     ecx,80*25*2/4
        mov     edi,0xB8000
        sub     edi,[ProgramBase]
        rep     stos dword [edi]
end if
        ;
        mov     esi,[HiHeapOrg]
        mov     edi,esi
        mov     ebx,esi
        add     edi,1M*1
        add     ebx,1M*2
        movzx   eax,[ThePSP1]
        add     eax,10h
        sub     [TheCS1],ax
        sub     [TheSS1],ax
        shl     eax,4
        cmp     word [esi+eax-100h],20CDh
        jnz     ErrNoValidPSP
        add     esi,eax
        movzx   eax,[ThePSP2]
        add     eax,10h
        sub     [TheCS2],ax
        sub     [TheSS2],ax
        shl     eax,4
        cmp     word [edi+eax-100h],20CDh
        jnz     ErrNoValidPSP
        add     edi,eax
        ;
        movzx   ecx,[ThePSP2]
        mov     edx,ecx
        movzx   eax,[ThePSP1]
        sub     ecx,eax
        mov     [Sum],cx
        add     edx,10h
        ;
        movzx   eax,[TheSS2]
        shl     eax,4
        cmp     eax,[MinSize]
        jnc     @F
        mov     [MinSize],eax
      @@:
        ;
        pushad
        mov     edx,MsgSizeDetect
        mov     ah,9
        int     21h
      .NoPSP:
        mov     ah,10h
        int     16h
        and     al,$DF
        cmp     al,'S'
        je      .Stack
        cmp     al,'P'
        jnz     .NoPSP
        movzx   ecx,[ThePSP1]
        movzx   eax,word [esi-100h+2]
        sub     eax,ecx
        movzx   ecx,[ThePSP2]
        movzx   edx,word [edi-100h+2]
        sub     edx,ecx
        cmp     eax,edx
        jbe     .Minimal
        mov     eax,edx
      .Minimal:
        sub     eax,10h
        shl     eax,4
        mov     [MinSize],eax
      .Stack:
        popad
        ;
        xor     ebp,ebp
        mov     ecx,[MinSize]
        ;
        mov     [OfsStart],edi
        mov     ax,LoopOdd
        test    byte [Sum],0xFF
        jnz     @F
        mov     ax,LoopNil
      @@:
        call    ax
        ;
        mov     [NbReloc],bp
        ;
        mov     ecx,ebp
        jecxz   @F
      .Loop2:
        mov     eax,[ebx+(ecx-1)*4]
        mov     dx,ax
        and     dx,0xF
        shl     eax,16-4
        mov     ax,dx
        mov     [ebx+(ecx-1)*4],eax
        loop    .Loop2
        ;
      @@:
        ;
        mov     eax,[MinSize]
        cmp     eax,0xFF00
        ja      TryGenerateExe
      TryGenerateCom:
        cmp     [NbReloc],0
        jnz     TryGenerateExe
        cmp     word [TheCS2+2],0x0100
        jnz     TryGenerateExe
        cmp     word [TheCS2+0],0xFFF0
        jnz     TryGenerateExe
        call    GenerateCom
        mov     ax,4C00h
        int     21h
      TryGenerateExe:
        mov     [Identificator],'MZ'
        call    GenerateExe
        mov     ax,4C00h
        int     21h

LoopNil:jecxz   .Break
        repz    cmps byte [esi],[edi]
        mov     ax,[edi-2]
        sub     ax,[esi-2]
        cmp     ax,[Sum]
        jne     .NoRelocation
        sub     [edi-2],dx
        lea     eax,[edi-2]
        sub     eax,[OfsStart]
        mov     [ebx+ebp*4],eax
        inc     bp
        jz      ErrErrorRelo
        jmp     LoopNil
      .NoRelocation:
        mov     eax,edi
        sub     eax,[OfsStart]
        mov     [MinSize],eax
      .Break:
        ret

LoopOdd:jecxz   .Break
        repz    cmps byte [esi],[edi]
        mov     ax,[edi-1]
        sub     ax,[esi-1]
        cmp     ax,[Sum]
        jne     .NoRelocation
        sub     [edi-1],dx
        lea     eax,[edi-1]
        sub     eax,[OfsStart]
        mov     [ebx+ebp*4],eax
        inc     bp
        jz      ErrErrorRelo
        cmps    byte [esi],[edi]
        sub     ecx,1
        jc      .Break
        jmp     LoopOdd
      .NoRelocation:
        mov     eax,edi
        sub     eax,[OfsStart]
        mov     [MinSize],eax
      .Break:
        ret

ErrNoValidPSP:
        mov     ah,9
        mov     dx,MsgNoValidPSP
        int     21h
        mov     ax,4C01h
        int     21h

ErrLoadStage1:
        mov     ah,9
        mov     dx,MsgLoadStage1
        int     21h
        mov     ax,4C01h
        int     21h

ErrLoadStage2:
        mov     ah,9
        mov     dx,MsgLoadStage2
        int     21h
        mov     ax,4C01h
        int     21h

ErrErrorRelo:
        mov     ah,9
        mov     dx,MsgErrorRelo
        int     21h
        mov     ax,4C01h
        int     21h

GenerateCom:
        mov     edx,TheCom
        call    Create
        mov     ecx,[MinSize]
        mov     edx,[HiHeapOrg]
        add     edx,1M
        movzx   eax,[ThePSP2]
        add     eax,10h
        shl     eax,4
        add     edx,eax
        call    Write
        call    Close
        ret

GenerateExe:
        ;
        mov     edx,[HiHeapOrg]
        add     edx,1M*1
        mov     eax,dword [TheCS2]
        rol     eax,16
        mov     dword [RegIP],eax
        mov     eax,dword [TheSS2]
        mov     dword [RegSS],eax
        ;
        mov     eax,28
        movzx   edx,[NbReloc]
        shl     edx,2
        add     eax,edx
        add     eax,15
        shr     eax,4
        mov     [TailleHeader],ax
        mov     [AddrRelocEXE],28
        ;
        shl     eax,4
        add     eax,[MinSize]
        xor     edx,edx
        mov     ecx,512
        div     ecx
        or      edx,edx
        jz      @F
        inc     eax
      @@:
        mov     [LongFicDiv],ax
        mov     [LongFicMod],dx
        ;
        mov     edx,TheExe
        call    Create
        mov     ecx,28
        mov     edx,Identificator
        call    Write
        movzx   ecx,[NbReloc]
        shl     ecx,2
        mov     edx,[HiHeapOrg]
        add     edx,1M*2
        call    Write
        ;
        movzx   eax,[NbReloc]
        shl     eax,2
        add     eax,28
        movzx   ecx,[TailleHeader]
        shl     ecx,4
        sub     ecx,eax
        mov     edx,Zeroes
        call    Write
        ;
        mov     ecx,[MinSize]
        mov     edx,[HiHeapOrg]
        add     edx,1M
        movzx   eax,[ThePSP2]
        add     eax,10h
        shl     eax,4
        add     edx,eax
        call    Write
        call    Close
        ret

Write:  pushad
        jecxz   .Ok
        mov     ebp,ecx
        mov     esi,edx
.Loop:  mov     ecx,DosBufferSize
        sub     ebp,ecx
        jnc     .Write
        add     ebp,ecx
        mov     cx,bp
        xor     ebp,ebp
.Write: push    cx
        mov     edi,[DosBuffer]
        shr     cx,2
        rep     movs dword [edi],[esi]
        mov     cx,[esp]
        and     cx,11b
        rep     movs byte [edi],[esi]
        pop     cx
        mov     ah,40h
        call    DosProcedure
        jc      .Error
        cmp     ax,cx
        jne     .Error
        or      ebp,ebp
        jnz     .Loop
        jmp     .Ok
.Error: stc
.Ok:    popad
        ret

Read:   pushad
        mov     ebp,ecx
        mov     edi,edx
.Loop:  mov     ecx,DosBufferSize
        sub     ebp,ecx
        jnc     .Read
        add     ebp,ecx
        mov     cx,bp
        xor     ebp,ebp
.Read:  push    cx
        mov     ah,3Fh
        call    DosProcedure
        mov     esi,[DosBuffer]
        mov     cx,[esp]
        mov     dx,ax
        push    cx
        lahf
        shr     cx,2
        rep     movs dword [edi],[esi]
        pop     cx
        and     cx,11b
        rep     movs byte [edi],[esi]
        sahf
        pop     ax
        jc      .Error
        cmp     ax,dx
        jne     .Error
        or      ebp,ebp
        jnz     .Loop
        jmp     .Ok
.Error: stc
.Ok:    popad
        ret

DosProcedure:
        push    dx
        mov     dx,TBuffer
        mov     ds,dx
        xor     dx,dx
        int     21h
        pop     dx
        push    cs
        pop     ds
        ret

Close:  mov     ah,3Eh
        int     21h
        ret

Seek0:  xor     edx,edx
SeekX:  mov     ah,42h
        mov     ecx,edx
        shr     ecx,16
        int     21h
        shl     edx,16
        xchg    dx,ax
        xchg    eax,edx
        ret

Open:   push    esi edi
        call    AdaptPath
        mov     esi,edx
        mov     ax,716Ch
        mov     bx,100000b
        mov     dx,1
        xor     ecx,ecx
        stc
        int     21h
        pushfd
        pop     [RegsEFL]
        cmp     ax,7100h
        je      OldOpen
        push    [RegsEFL]
        popfd
        jnc     OpenDone
      OldOpen:
        mov     ax,3D00h
        mov     edx,esi
        int     21h
      OpenDone:
        xchg    ebx,eax
        pop     edi esi
        ret
    AdaptPath:
        mov     esi,edx
        mov     edi,Buffer
      CopyPath:
        lodsb
        cmp     al,'/'
        jne     PathCharOK
        mov     al,'\'
      PathCharOK:
        stosb
        or      al,al
        jnz     CopyPath
        mov     edx,Buffer
        ret

Create: push    esi edi
        call    AdaptPath
        mov     esi,edx
        mov     ax,716Ch
        mov     bx,100001b
        mov     dx,10010b
        xor     ecx,ecx
        xor     edi,edi
        stc
        int     21h
        pushfd
        pop     [RegsEFL]
        cmp     ax,7100h
        je      OldCreate
        push    [RegsEFL]
        popfd
        jnc     CreateDone
      OldCreate:
        mov     ah,3Ch
        mov     edx,esi
        xor     ecx,ecx
        int     21h
      CreateDone:
        xchg    ebx,eax
        pop     edi esi
        ret
if 0
GetValue:
        xor     eax,eax
        xor     edx,edx
        mov     ecx,4
        mov     ebx,XLATX
     .Loop:
        lodsb
        xlatb
        shl     edx,4
        add     edx,eax
        loop    .Loop
        mov     eax,edx
        ret

GetParams:
        mov     ax,3
        int     10h
        mov     eax,7FB07FB0h
        mov     ecx,80*25*2/4
        mov     edi,0xB8000
        sub     edi,[ProgramBase]
        rep     stos dword [edi]

        mov     edx,13
        mov     ebx,32
        mov     edi,0xB8000+(80-32)+(80*2*5)
        sub     edi,[ProgramBase]
        call    DrawBorder

        mov     edi,0xB8000+(80-32)+(80*2*(6+5))
        sub     edi,[ProgramBase]
        mov     ah,7Fh
        mov     al,0xC3
        stos    word [edi]
        mov     al,0xC4
        mov     ecx,30
        rep     stos word [edi]
        mov     al,0xB4
        stos    word [edi]

        mov     esi,Msg1st
        mov     edi,0xB8000+(80-32)+(80*2*(5+0))+4
        sub     edi,[ProgramBase]
        mov     ah,7Bh
        call    MoveWord.LoopX

        mov     esi,Msg2nd
        mov     edi,0xB8000+(80-32)+(80*2*(5+6))+4
        sub     edi,[ProgramBase]
        mov     ah,7Bh
        call    MoveWord.LoopX

      .DrawMsg:

        mov     dh,6
        mov     dl,32
        add     dh,[CurY]
        add     dl,[CurX]
        call    SetCursorPos

        mov     edi,0xB8000+(80-32)+(80*2*(6+0))+4
        sub     edi,[ProgramBase]
        call    DrawMsgs1

        mov     edi,0xB8000+(80-32)+(80*2*(6+6))+4
        sub     edi,[ProgramBase]
        call    DrawMsgs2

     .Fatal:
        mov     ah,10h
        int     16h
        cmp     ax,0E08h
        jz      .IsBkSpc
        cmp     ax,0F09h
        jz      .IsTab
        cmp     ax,0F00h
        jz      .IsShiftTab
        cmp     ax,1C0Dh
        jz      .IsEnter
        cmp     ax,011Bh
        jz      .IsEsc
        cmp     al,'0'
        jb      .Fatal
        cmp     al,'9'
        jbe     .Valid
        and     al,0xDF
        cmp     al,'A'
        jb      .Fatal
        cmp     al,'F'
        ja      .Fatal
      .Valid:
        cmp     [CurX],4
        jae     .Fatal
        movzx   edx,[CurY]
        movzx   edx,[edx*2+CurZ]
        movzx   ecx,[CurX]
        mov     [edx+ecx],al
        inc     [CurX]
        jmp     .DrawMsg
      .IsBkSpc:
        cmp     [CurX],0
ifa     dec     [CurX]
        jmp     .DrawMsg
      .IsEnter:
        mov     [CurX],0
        inc     [CurY]
        cmp     [CurY],5
ifz     inc     [CurY]
        cmp     [CurY],10
        jbe     .DrawMsg
      .IsEsc:
        ret
      .IsShiftTab:
        sub     [CurY],1
ifc     mov     [CurY],10
        cmp     [CurY],5
ifz     dec     [CurY]
        jmp     .DrawMsg
      .IsTab:
        mov     [CurX],0
        inc     [CurY]
        cmp     [CurY],5
ifz     inc     [CurY]
        cmp     [CurY],10
ifz     mov     [CurY],0
        jmp     .DrawMsg

SetCursorPos:
        mov     ah,2
        xor     bh,bh
        int     10h
        ret

MoveWord:
        mov     ah,7Eh
      .LoopX:
        lodsb
        or      al,al
        jz      .Break
        stos    word [edi]
        jmp     .LoopX
      .Break:
        ret

DrawMsgs1:
        push    edi
        mov     esi,MsgCS1
        call    MoveWord
        mov     edi,[esp]
        add     edi,80*2
        mov     esi,MsgIP1
        call    MoveWord
        mov     edi,[esp]
        add     edi,80*4
        mov     esi,MsgSS1
        call    MoveWord
        mov     edi,[esp]
        add     edi,80*6
        mov     esi,MsgSP1
        call    MoveWord
        mov     edi,[esp]
        add     edi,80*8
        mov     esi,MsgPSP1
        call    MoveWord
        pop     edi
        ret

DrawMsgs2:
        push    edi
        mov     esi,MsgCS2
        call    MoveWord
        mov     edi,[esp]
        add     edi,80*2
        mov     esi,MsgIP2
        call    MoveWord
        mov     edi,[esp]
        add     edi,80*4
        mov     esi,MsgSS2
        call    MoveWord
        mov     edi,[esp]
        add     edi,80*6
        mov     esi,MsgSP2
        call    MoveWord
        mov     edi,[esp]
        add     edi,80*8
        mov     esi,MsgPSP2
        call    MoveWord
        pop     edi
        ret

DrawBorder:
        push    edi
        mov     al,0xDA
        stos    word [edi]
        mov     al,0xC4
        lea     ecx,[ebx-2]
        rep     stos word [edi]
        mov     al,0xBF
        stos    word [edi]
        pop     edi
        add     edi,80*2
        lea     ecx,[edx-2]
        jecxz   .Q
      .Loop:
        push    edi
        mov     al,0xB3
        stos    word [edi]
        mov     al,0x20
        push    ecx
        lea     ecx,[ebx-2]
        rep     stos word [edi]
        pop     ecx
        mov     al,0xB3
        stos    word [edi]
        pop     edi
        add     edi,80*2
        loop    .Loop
      .Q:
        mov     al,0xC0
        stos    word [edi]
        mov     al,0xC4
        lea     ecx,[ebx-2]
        rep     stos word [edi]
        mov     al,0xD9
        stos    word [edi]
        ret

CurZ    dw      MsgCS1+6
        dw      MsgIP1+6
        dw      MsgSS1+6
        dw      MsgSP1+6
        dw      MsgPSP1+6
        dw      0
        dw      MsgCS2+6
        dw      MsgIP2+6
        dw      MsgSS2+6
        dw      MsgSP2+6
        dw      MsgPSP2+6

MsgCS1          db 'CS  : 0000',0
MsgIP1          db 'IP  : 0000',0
MsgSS1          db 'SS  : 0000',0
MsgSP1          db 'SP  : 0000',0
MsgPSP1         db 'PSP : 0000',0
MsgCS2          db 'CS  : 0000',0
MsgIP2          db 'IP  : 0000',0
MsgSS2          db 'SS  : 0000',0
MsgSP2          db 'SP  : 0000',0
MsgPSP2         db 'PSP : 0000',0
Msg1st          db '1st file',0
Msg2nd          db '2nd file',0
end if

MsgLoadStage1   db 'error: can not load "stage.1st"',13,10,36
MsgLoadStage2   db 'error: can not load "stage.2nd"',13,10,36
MsgErrorRelo    db 'error: too many relocations!!!',13,10,36
MsgNoValidPSP   db 'error: no valid PSP found!!!',13,10,36
MsgSizeDetect   db 'Autodetect file size by PSP or Stack, [P/S] ?',13,10,36

Identificator   dw 'MZ'
LongFicMod      dw 0            ; file length MOD 512
LongFicDiv      dw 0            ; file length DIV 512
NbReloc         dw 0            ; relocations numbers
TailleHeader    dw 0            ; hedear size in paragraphs
ParMin          dw 0            ; paragraphs minimal number
ParMax          dw -1           ; paragraph maximal number
RegSS           dw 0            ; initial SS
RegSP           dw 0            ; initial SP
Checksum        dw 0            ; EXE header checksum
RegIP           dw 0            ; initial IP
RegCS           dw 0            ; initial CS
AddrRelocEXE    dw 0
NumOvl          dw 0

if 0
CurX            db 0
CurY            db 0
XLATX           db 256 dup 0
store Byte 00h at (XLATX+'0')
store Byte 01h at (XLATX+'1')
store Byte 02h at (XLATX+'2')
store Byte 03h at (XLATX+'3')
store Byte 04h at (XLATX+'4')
store Byte 05h at (XLATX+'5')
store Byte 06h at (XLATX+'6')
store Byte 07h at (XLATX+'7')
store Byte 08h at (XLATX+'8')
store Byte 09h at (XLATX+'9')
store Byte 0Ah at (XLATX+'A')
store Byte 0Bh at (XLATX+'B')
store Byte 0Ch at (XLATX+'C')
store Byte 0Dh at (XLATX+'D')
store Byte 0Eh at (XLATX+'E')
store Byte 0Fh at (XLATX+'F')
end if
Buffer          rb 128
Params          rb 128
DosBuffer       dd ?
RegsEFL         dd ?

Size1st         dd ?
Size2nd         dd ?
MinSize         dd ?
OfsStart        dd ?

ThePSP1         dw ?
TheCS1          dw ?,?
TheSS1          dw ?,?
ThePSP2         dw ?
TheCS2          dw ?,?
TheSS2          dw ?,?

Sum             dw ?

segment _BIOS_  use16
BISRs   rd      256
segment TBuffer use16
BufStr  rb      DosBufferSize
segment Stck    use16
        rb      4k-4
EndStack:
