; ****************************************************************************
; *                                                                          *
; *                       IBM/Microsoft Confidential                         *
; *                                                                          *
; *                 Copyright (c) IBM Corporation  1987, 1996                *
; *                 Copyright (c) Microsoft Corp.  1987, 1991                *
; *                           All Rights Reserved                            *
; *                                                                          *
; ****************************************************************************
;
; Change History
;
;  5/03/96  153804  cgw  unhybernate via int 13, ifdef'd on HYBERI13
;  5/25/96  155605  cgw  clean up debug msgs to reduce memory use
;  5/31/96  155895  MT   save register (di) for APM system
;  6/17/96  156656  TPL  Change "Restoring OS/2...." to "-> OS/2..."
;                        to avoid translation and code change
;  7/19/96  159979  TPL  Correct drive light problem on hibernation resume
;  8/03/96  160195  MT   New ArenaInfo record to save IVT, BIOS Data and XBDA
;  8/05/96  161648  GCM  Faster Intel Machines fail to boot
;  8/16/96  166339  MT   Check for non-XBDA machines
; ****************************************************************************

        page    ,132
        name    main

.286p
;;;;;;;;;TIMELOAD  EQU  TRUE

        .xlist
        include segments.inc    ; segment declarations
        include basemaca.inc
        include dbprtdef.inc    ; DBPRT macro definition
        include doshlp.inc      ; doshlp macros
        include filetab.inc
        include mi.inc
        include seldesc.inc
        include sysinvar.inc
        include basemid.inc
        include timer.inc       ;Physical Timer 0 (Tmr) defs.
ifdef   TIMELOAD
        include out.inc
endif
        include int.inc
        include hybernat.inc
        include hyberhd.inc

RTCCTL          equ     070h
RTCDTA          equ     071h
TIMER_0         equ     040h
TIMER_1         equ     041h
TIMER_2         equ     042h
TIMERCB         equ     043h                    ; timer control byte

DELAY   macro
        local   dly

        push    ecx
        ; arbitrary value - just to let registers settle a bit
        mov     ecx, 0ffffh
dly:
        loop    dly
        pop     ecx
        endm

if HYBERDEBUG_R GE 2
; assumes edx = screen position
showEAX macro
        local   eax1, eax2, eax3
        pushad

        mov     ecx, 28

eax1:
        mov     ebx, eax
        shr     ebx, cl
        and     ebx, 0fh
        cmp     ebx, 0ah
        jge     eax2
        or      ebx, 0e30h
        jmp     eax3
eax2:
        add     ebx, 37h
        or      ebx, 0e00h
eax3:
        mov     [edx], bx
        add     edx, 2
        sub     ecx, 4
        jge     eax1

        popad
        add     edx, 20
        endm
endif ;HYBERDEBUG_R GE 2

        .list

.8086                   ; 8086 opcodes only until CPU verified

        extrn   _loader:near
        extrn   _loadmsgs:near
        extrn   _setldrframe:near
        extrn   _cgetdhphysmem:near
        extrn   BootDDInit:near
        ;extrn   getdhsysdmp:near    ; dumpdump
        extrn   getdhconfig:near
        extrn   getdhphysmem:near
        extrn   getdhgetdp:near
        extrn   getvideoinfo:near
        extrn   DHEnableA20:near
        extrn   RealGetMessage:near
        extrn   IODFixup:near
        extrn   dhRemapPIC:near
        extrn   InitPIC:near
ifndef HYBERI13
        extrn   _unhybernate:near
else  ;HYBERI13
        extrn   unhyber:near
endif ;HYBERI13

ifdef PMDEBUG
    ifdef 0
        extrn   _dumpmsvars:near
    endif
    ifdef CALLDEBUGGER
        extrn   Debug_Entry:near
        extrn   _dbflag:word
    endif
endif

ifdef OS2MEM
        extrn   _fakemem:near
        extrn   _cgetdhphysmem:near
endif

        public  __acrtused
__acrtused equ  1               ; C compiler puts out an extern


STARTBSS segment
        db      "bss",0         ; force all prior data to be initialized
        even
StartBss label byte             ; mark start of zero'd data
STARTBSS ends

PAGESIZE        equ     4096
PAGEMASK        equ     PAGESIZE - 1
PAGESHIFT       equ     12

;; Warning - this constant is hardcoded into LOADER.C - change in both places.
STACKMAGIC      equ     05ee5h

        public _StackFence, _TopOfStack
STACK segment
                even
_StackFence     dw      ?       ; Trashed if stack overrun
ifndef HYBERI13
                dw      1280 dup (?)
else  ;HYBERI13
if HYBERDEBUG_R EQ 0
                dw      1280 dup (?)   ; HYBERDEBUG_R = 0 (retail)
else
                dw      1024 dup (?)   ; HYBERDEBUG_R >= 1 (debug)
endif
endif ;HYBERI13



_TopOfStack      equ     $
STACK ends


DosHlpData segment

        extrn   _bHighSpeed:byte
ifdef SAVEDOSIVT_OLD                    ; 160195
        extrn   DosIVT:word             ; 160195
else                                    ; 160195
        extrn   SavedBIOSSeg:word       ; 160195
endif                                   ; 160195

        public  _DHMachModel, _DHMachSubModel
MachModelSubModel label   word
_DHMachSubModel   db      ?              ; MachModel and SubModel used in
_DHMachModel      db      ?              ;   DosHlp code to select proper code

DosHlpData ends

OEMHLP_CODE segment
        extrn   OEMHLP_STRATEGY:far
OEMHLP_CODE ends

_DATA segment
        extrn   BootBPB:byte
        extrn   BootDrive:byte
        extrn   _LowMem:word
        extrn   _HighMem:word
        extrn   _ExtendMem:Dword
        extrn   _hybernation:word
ifndef  HYBERI13
        extrn   _hyberInfoHdr:byte
else   ;HYBERI13
        extrn   _fphyberInfoHdr:Dword
endif  ;HYBERI13
        extrn   PIC1Init:word
        extrn   PIC2Init:word

        public  _SysInitSeg

_SysInitSeg     dw      0
ldrend          dw      0
ifndef  HYBERI13
ProtGDT         dw      0
ProtGDT2        dd      0
endif  ;HYBERI13

                public _BIOSSeg                 ; will be set by loader.c ;160195
_BIOSSeg        dw      0                       ;                         ;160195
restoreMsg      db      '--> OS/2...', 0h

comment *

========== DosHlp related information ==========

DOSHLP_CODESEL      100h - used for resident DosHlp code (Tiled)
DOSHLP_DATASEL      108h - used for resident DosHlp data
selSysInitCode      110h - used for discardable DosHlp code
selSysInitData      118h - used for discardable DosHlp data

Discardable DosHlps
===================
To create a discardable DosHlp - used for those only used once during kernel
boot that can be discarded to save memory follow these rules:-

    1 Put discardable Code in _TEXT or DosHlpDisc32Code Segment

    2 Put discarable Data in _Data Segment

    3 Use DefDiscDosHlp macro in DosHlpFunctions table below (NOT DefDosHlp)

    4 If DosHlp to be called in RealMode add segment fixup line to function
      PatchDosHlpTable (see main.asm)

    5 Add line to Sysinit_Discard_DosHlp (inidin1.asm) to point to internalerror
      (they are discarded at the end of Sysinit_Initialize_Dos).

    6 <<< Warning - We don't support discardable DosHlp that is called from both
      real and protect mode >>>.

32-bit DosHlps
==============
Doshlp functions that will be called with 32-bit address needs to have
the address fixed up in the function SysInit_DosHlpFix32 (inidin2.asm).

*


; DosHlp function jump table

; Note -- any changes in the order of these functions, or additions of
;         functions must be reflected in the DosHlpFunction structure in
;         doshlp.inc

        public  DosHlpFunctions
DosHlpFunctions label word

; Version number - used to verify an os2ldr and os2krnl match
        dw              DOSHLPVERSION

; DosHlp Initialization
        DefDiscDosHlp   Init

; reboot control
        DefDosHlp       Reboot

; NMI Enabling/Disabling
        DefDosHlp       NMI

; available memory recognition
        DefDiscDosHlp   SizeMem

; Get hardware configuration
        DefDiscDosHlp   Config

; Return a pointer to the base (virtual) device driver list
        DefDiscDosHlp   BaseDDList

; Get Disk Parameters
        DefDiscDosHlp   GetDriveParms

; stand alone dump
        DefDosHlp       InitSystemDump
        DefDosHlp       SystemDump

; Real mode read sectors
        DefDiscDosHlp   ReadSectors

; Serial I/O
        DefDosHlp       SerInit
        DefDosHlp       SetBaudRate
        DefDosHlp       SerIn
        DefDosHlp       SerOut

; Speaker Control
        DefDosHlp       ToneOn
        DefDosHlp       ToneOff

; Mode switch control
        DefDosHlp       GetMask
        DefDosHlp       SetMask
        DefDosHlp       SetRealMask
        DefDosHlp       SetProtMask
        DefDosHlp       SetDosEnv

; Character IO
        DefDosHlp       CallInt10

; Get Message text

        DefDosHlp       ProtGetMessage  ; For protected mode usage
        DefDiscDosHlp   RealGetMessage  ; For real mode usage

; Tmr (PIT Ctr 0) device
        DefDiscDosHlp   RegisterTmrDD
        DefDosHlp       Tmr16QueryTime

; Watchdog NMI

        DefDosHlp       EnableWatchdogNMI
        DefDosHlp       DisableWatchdogNMI

; Hardware interrupt preinitialization

        DefDiscDosHlp   InstallIRET

; Discard DosHlps

        DefDiscDosHlp   Discard

;========= 32-bit Doshlps

; interrupt control
        DefDiscDosHlp   InitInterrupts
        DefDosHlp       SetIRQMask
        DefDosHlp       SendEOI

; Tmr (PIT Ctr 0) device
        DefDosHlp       Tmr32QueryTime
        DefDosHlp       TmrSetRollover

; coprocessor support
        DefDiscDosHlp   InitNPX
        DefDosHlp       ClrBusyNPX
        DefDosHlp       AckIntNPX
        DefDosHlp       WaitNPX
        DefDosHlp       ValidNPXSwitch
        DefDosHlp       VNPXReset
        DefDosHlp       VNPXClrBusy

; NMI message display
        DefDosHlp       WhyNMI

; NMI Acknowledgement
        DefDosHlp       AckNMI

; Watchdog NMI
        DefDosHlp       ResetWatchdogNMI

; Parity
        DefDosHlp       DisableCache
        DefDosHlp       FindParity
        DefDosHlp       EnableCache
        DefDosHlp       GetErrorLogPtr
        DefDosHlp       WriteErrorLog
        DefDosHlp       ReadErrorLog
        DefDosHlp       ResetParity



ifdef OS2MEM
OS2MEMErrMsg    db      'OS2MEM specifies more memory than available',0dh, 0ah, 0
endif
CrLfMsg         db      0dh, 0ah, 0
esditmp         dd      ?                       ; must NOT be in _BSS
twodiskboot     dw      ?       ; 0: one disk boot; 1: two disk boot

Version         db      'OS2LDR 206 08Feb93'

ifdef PMDEBUG
        public  sawctlc
sawctlc         db      0                       ; must NOT be in _BSS
indebugger      db      0                       ; must NOT be in _BSS
endif

            public  _IODelayCnt

_IODelayCnt     dw      ?

ifndef   HYBERI13
;;;;; MINI GDT
mungegdt        dw      0,     0,     0,    0
                dw      0FFFFh,0,09200h,008fh  ;base=0, limit=4gig
;;;;;MINIGDT

gdtptr          dw      16,0,0
endif  ;HYBERI13

WRTSTRING       DB      0DBh,07h,0DBh,07h,0DBh,07h,0DBh,07h
                DB      ' ',07h,'O',07h,'S',07h,'/',07h,'2',07h
_DATA ends




_BSS segment
        public  _OS2Init, _BootFlag, _filetable
_OS2Init        dd      ?       ; kernel entry address
ComPortAddr     dw      ?
_BootFlag       db      ?       ; Boot_flag = 0 - FAT boot

ifdef PMDEBUG
_BreakChar      db      ?       ; possible kernel debugger break character
endif

blockmovegdt    desctab <>      ; first descriptor is not accessible
                desctab <>      ; location of gdt
sourcedesc      desctab <>      ; source descriptor
targetdesc      desctab <>      ; target descriptor
                desctab <>      ; bios code descriptor
                desctab <>      ; bios stack descriptor

GDTSIZE         equ     (($ - blockmovegdt)/size desctab)

_filetable      filetable_s <>  ; file table offset
_MsInitVars     MsInitVars_s <> ; initialization packet

_BSS ends


_TEXT segment
    assume cs:DGROUP




;***    EarlyDelay - Delay code until iodelay loop calibrated
;

EarlyDelay PROC Near
        ; Used to be 15 "jmp $+2" instructions for 50 MHz Compaqs
        ; The delay of 15 is a total guess for 66 MHz double play.
        push    ax
;       mov     ax,15
;       mov     ax,30
        mov     ax,0ffh       ;161648 Delay HACK for Faster Machine
EDRpt:  dec     ax
        jnz     EDRpt
        pop     ax
        ret
EarlyDelay ENDP




;***    main - load and relocate the OS2KRNL image
;
;       This routine is the entry point for OS2LDR, which is responsible for
;       loading the OS2KRNL image and initializing OS2KRNL and mFSD.
;
;       The boot sector jumps (via far jump) to the OS2LDR entry point,
;       which makes a near jump to this routine.
;
;       OS2LDR relocates itself to the top of low memory.
;
;       For FAT-based boot systems, OS2LDR scans the root directory for
;       OS2KRNL, and uses the BIOS' interrupt 13h handler to read OS2KRNL
;       from disk/diskette into memory.
;
;       For non-FAT-based boot systems, OS2LDR calls the micro-FSD to open
;       and read OS2KRNL.
;
;       After the required fixups are applied to OS2KRNL, control is
;       transferred to OS2KRNL.
;
;       When OS2LDR receives control for a FAT boot system, OS2LDR expects
;       the memory map to be as as follows:
;
;               +-------------+ Top of High Memory
;               |   unused    |
;               +-------------+ 1Meg
;               | ROM/screen  |
;               +-------------+ Top of Low Memory
;               |   unused    |
;               +-------------+
;               |   OS2LDR    |
;               +-------------+ 20000h (128k)
;               |  Root Dir.  |
;               +-------------+ 10000h (64k)
;               |  reserved   |
;               +-------------+  0
;
;       When OS2LDR receives control for a non-FAT boot system, OS2LDR expects
;       the memory map to be as as follows:
;
;               +-------------+ Top of High Memory
;               |   unused    |
;               +-------------+ 100000h (1Meg)
;               | ROM/screen  |
;               +-------------+ Top of Low Memory
;               | 64k unused  |
;               +-------------+ Top of Low Memory - 64k
;               |  microFSD   |
;               +-------------+
;               |   unused    |
;               +-------------+
;               |   OS2LDR    |
;               +-------------+ 10000h (64k)
;               |  mFSD.FSD / |
;               |  RIPL data  |
;               +-------------+   800h (2k)
;               |  reserved   |
;               +-------------+  0
;
;       OS2LDR will relocate remote IPL data to the top of high memory
;       (if RIPL required) and the mFSD to the top of available high
;       memory (just below the remote IPL data if RIPL required), apply
;       the required fixups to OS2KRNL, and transfer the control to OS2KRNL.
;       The memory map will be as follows:
;       Note that "holes" in high memory are supported.
;       Note also that the kernel may or may NOT be loaded at 1 meg.
;
;               +-------------+ Top of High Memory
;               |  RIPL data  | (non-FAT boot only)
;               +-------------+
;               |  mFSD.FSD   | (non-FAT boot only)
;               +-------------+
;               |    free     |
;               +-------------+
;               | OS2KRNL(hi) |
;               +-------------+
;               |    free     |
;               +-------------+ 1Meg
;               | ROM/screen  |
;               +-------------+ Top of Low Memory
;               |   OS2LDR    |
;               +-------------+
;               |    free     |
;               +-------------+
;               | OS2KRNL(lo) |
;               +-------------+
;               |  DosHlpSeg  |
;               +-------------+ 1000h
;               |  reserved   |
;               +-------------+ 0
;
;       NOTES:  OS2LDR runs in real mode.
;               The sum of the mFSD and RIPL data sizes must be less than 62k.
;
;       ENTRY   (CS:IP) = entry point address
;                       = 2000:0000H for FAT boot system
;               (SS:SP) = 0030:00FAH for FAT boot system (CBIOS dependent)
;               (BX)    = First data sector on the disk (0 based)
;               (DH)    = boot flags:
;                           0 indicates FAT-based boot
;                           BF_NOMFSHVOLIO 01h (bit 0)
;                             set if mini-FSD does not use mFSH_DOVOLIO
;                           BF_RIPL        02h (bit 1)
;                             set if boot volume is not local (RIPL boot)
;                           BF_MINIFSD     04h (bit 2)
;                             set if mini-FSD is present
;                             (required for non-FAT boot)
;                           BF_MICROFSD    08h (bit 3)
;                             set if micro-FSD is present
;                             (new for cruiser - required for non-FAT boot)
;                           BF_RESERVED    f0h (bits 4-7) must be 0
;               (DL)    = drive number for the boot disk
;                           ignored if (DH & BF_NOMFSHVOLIO) == 0 ||
;                                      (DH & BF_MINIFSD) == 0
;                           if (DH[2] == 0 or DH[0] == 0)
;               (DS:SI) = address of BOOT MEDIA'S bpb
;                           ignored if (DH & BF_NOMFSHVOLIO) == 0 ||
;                                      (DH & BF_MINIFSD) == 0
;                           if (DH[2] == 0 or DH[0] == 0)
;               (ES:DI) = address of root directory for FAT-based boot
;                       = else address of file table (see filetab.h)
;       EXIT    Jmp to OS2KRNL entry point
;               (SS:BX) = address of parameter packet (see sysinvar.h)
;
;       EFFECTS The segments within OS2KRNL are loaded into memory
;
;       MESSAGES
;                The sytem detected an 8086 or 80286 CPU, ...
;                A disk read error occurred.
;                The system cannot find the OS2KRNL file.
;                The system does not have enough storage to
;                    start IBM Operating System/2.
;                Insert a system diskette and restart the system.
;                The file OS2KRNL is not acceptable.
;                The data to ipl the system is not acceptable.
;
;       Pseudo Code:
;       disable interrupts
;       setup ss:sp to point to top of the stack
;       enable interrupts
;       call initcom to initialize the com ports
;       store boot flag
;       verify we are on a good CPU
;       if mini-FSD exists
;          copy the file table to local data area
;       endif
;
;       call BootDDInit to initialize for disk activity
;       call dhgetdhconfig for information needed by DHGetConfig
;
;       call dhgetphysmem for information on physical mem config
;
;       get model & sub-model
;       put model & sub-model in DosHlp data segment
;
;       call _setldrframe to set the memory location for the os2ldr
;        image, detects if >16M on EISA machines
;
;       call _loadmsgs - determine if normal boot or unHybernating
;         if normal load the message file (includes initializing the FAT)
;
;       if unHybernating go unHybernat
;
;       call IndicateAltF1 to indicate ALT-F1 can be pressed
;
;       if on COMPAQ
;           set high speed
;
;       call getdhsysdmp for information needed by DHSysDmp
;       call getdhgetdp for information needed by DHGetDriveParams
;       call getvideoinfo for information needed by the OEMHLP$ video
;        information request
;
;       call _loader to load/fix-up OS2KRNL, and
;        relocate mini-FSD if mini-FSD exists
;
;       call PatchDosHlpTable to fix up the discardable doshlp addresses
;       call dhRemapPIC to cahnge pic 1 base vector
;
;       setup registers and give control to OS2KRNL
;


Procedure main,far
        assume ds:nothing,es:nothing,ss:nothing
        cld

;       (BX)    = First data sector on the disk (0 based)
;       (DH)    = boot flags (0 for FAT boot)
;       (DL)    = drive number for the boot disk
;       (DS:SI) = address of BOOT MEDIA'S bpb
;       (ES:DI) = address of file table (root directory for FAT boot)

        mov     [esditmp].Segmt,es      ; save filetable/rootdir pointer
        mov     [esditmp].Offst,di
        mov     ax,cs                   ; (es:di) = address of base of BSS
        mov     es,ax
        mov     di,offset StartBss
        mov     cx,offset _StackFence    ; (cx) = word count to clear
        sub     cx,di
        shr     cx,1
        xor     ax,ax
        rep stosw                       ; clear BSS

        ; Set up the top of the loader stack. This also locates the kernel
        ; ArenaInfo array which starts just after the loader.
        ; The following causes it to be allocated on a frame boundry.
        ; By doing so, it should only consume 1 frame of kernel memory.
        mov     ax, offset DGROUP:_TopOfStack ; length of ldr image
        add     ax, PAGESIZE - 1
        and     ax, not PAGEMASK
        mov     ldrend, ax      ; marks the ldr tos and beginning of ArenaInfo
        cli
        mov     sp, ax
        mov     ax, cs
        mov     ss, ax                   ; switch stacks
        sti
        assume ss:DGROUP

        mov     [_StackFence],STACKMAGIC ; tested later to detect overrun

        push    ds                      ; save ds,si
        push    si

        mov     ds,ax                   ; set up data access for _BootFlag
                                        ;  and debug testing
        assume ds:DGROUP

        call    initcom                 ; initialize the com ports

        mov     [_BootFlag],dh          ; store boot flag

        call    CpuTest                 ; verify we're running on a 386

.386p                                   ; now it's ok to use 386 opcodes

        DBPRT   17,1,<OS2LdrD 206 08Feb93\n>
        DBPRT   17,1,<ComPort $x\n>,ComPortAddr

ifdef   TIMELOAD
        out_crlf
        DBPRT   17,1,<Start Time\n>
        call    PrintTime
endif

        ; Zero FS and GS. Zero extend all other registers
        xor     eax, eax
        mov     fs, ax
        mov     gs, ax
        movzx   ebx, bx
        movzx   ecx, cx
        movzx   edx, dx
        movzx   esi, si
        movzx   edi, di
        movzx   esp, sp
        movzx   ebp, bp

        call    getdhconfig             ; Get info required by DHGetConfig

        ; Now that we know what type of machine we are running on, determine
        ; if that machine needs to be set to HIGH speed and call the keyboard
        ; ROM to perform the operation
        ;
        ; Note: we need to perform this before the IODelay calibration
        ;       to ensure that the machine is running at high speed

        cmp     _bHighSpeed, MACH_COMPAQ
        jne     short main15
        mov     ax,SETSPEED_HIGH        ; sets speed to HIGH (fastest)
        int     16h

main15:
        call    IODELAYCalibrate
        call    IODFixup
        DBPRT   17,1,<IODel $x\n>,_IODelayCnt


;       if mini-FSD exists, copy the file table to local data area

        or      dh,dh                   ; Does mini-FSD exist?
        jz      short mai20             ; No, jump
        lds     si,[esditmp]            ; (ds:si) = filetable pointer
        mov     ax,cs                   ; (es:di) -> local file table
        mov     es,ax
        mov     di,offset _filetable
        mov     cx,(size filetable_s) shr 1
        .errnz ((size filetable_s) and 1)
        rep movsw

mai20:
        pop     si                      ; restore ds,si
        pop     ds
    assume ds:NOTHING


        les     di,[esditmp]            ; (ds:si) = rootdir pointer

;       (BX)    = LSN of the first sector of cluster # 2
;       (DL)    = int 13 drive number we booted from
;       (DH)    = boot flags
;       (DS:SI) -> the boot media's BPB
;       (ES:DI) -> root directory (if BootFlag == 0)

       ;DBPRT   16,1,<calling BootDDinit\n>
        call    BootDDInit
       ;DBPRT   16,1,<returning from BootDDinit\n>
        mov     twodiskboot,ax          ; flag if disk swap required later

        push    ss
        pop     ds
        assume ds:DGROUP

        DBPRT  -1,1,<Model $x\n>,MachModelSubModel

        cmp     ax,1                    ; Check if booting during install from
                                        ;   disk
        jne     short main25            ; Jmp if no !
        call    UpdateBelmontCMOS       ;

main25: call    getdhphysmem            ; get physical mem config

;       adjust load memory limit

        mov     ax, ldrend
        push    ax
        call    _setldrframe
        inc     sp                      ; clean up
        inc     sp

ifdef OS2MEM
        mov     ax, ldrend
        push    ax
        push    cs                      ; Loader segment
        call    _fakemem                ; (fakemem in OS2MEM.C)
        add     sp,4

        ; reset arena info with fake memory information.

        call    _cgetdhphysmem          ; (cgetdhphysmem in CDOSHLP.C)
endif

if HYBERDEBUG_R GE 1
        ; clear bios post stuff from screen
        mov     ax, 0600h
        mov     bx, 0e00h
        xor     cx, cx
        mov     dx, (24*100h)+79
        int     10h
        mov     ax, 0200h
        xor     bx, bx
        xor     dx, dx
        int     10h
endif ;HYBERDEBUG_R GE 1

;       Load message file

        push    offset _TEXT            ; DosHlp length
        mov     ax, ldrend
        push    ax                      ; Loader Length
        push    cs                      ; Loader segment
       ;DBPRT   1+16,1,<calling _loadmsgs\n>
        call    _loadmsgs
        add     sp,6                    ; clean up
       ;DBPRT   1+16,1,<calling getdhsysdmp\n>
                                        ; dumpdump
       ;call    getdhsysdmp             ; save bios data and vectors dumpdump

       ;DBPRT   1+16,1,<calling getdhgetdp\n>
        call    getdhgetdp              ; Get info required by DHGetDriveParms

        ; Get info required by OemHlp$'s video information request
        ;
        ; Getvideoinfo will do an int 10 1130h to get the address
        ; of video buffers and will save the results in OEMHLP$'s data segment.
        ; Later users in protect mode can get these saved values.
        ;
        ; This function is called here in real mode so it can do INT 10s
        ; or any other hardware interrupt.  Other functions could be added
        ; here to do similar things.

        DBPRT   17,1,<cgvi\n>
        call    getvideoinfo

        cmp     [_hybernation],0
        jz      main2

ifndef HYBERI13
        mov     ax, ComPortAddr         ; pass on the debugger port address
        push    ax
        mov     ax, ldrend
        push    ax
        push    cs                      ; Loader segment
        cli
        DBPRT   1+16,1,<calling _unhybernate\n>
        call    _unhybernate
        add     sp,6                    ; clean up
endif ;HYBERI13

        DBPRT   17,1,<a20\n>
        call    DHEnableA20

        ; turn off the floppy light
        mov     dx, 3f2h
        mov     al, 0ch                 ; 159979
        out     dx, al

        ; if APM then reconnect
ifndef  HYBERI13
        test    _hyberInfoHdr.hih_hyberflags, HF_APM16
else   ;HYBERI13
        les     di,_fphyberInfoHdr
        push    di                      ; save di because INT 15h WILL
                                        ;    MODIFY IT. 155895
        test    es:hyberInfoHdr_s.hih_hyberflags[di], HF_APM16
endif  ;HYBERI13
        jz      apm1
        DBPRT   17,1,<connect apm 16\n>
        mov     ax, 5302h
        xor     bx, bx
        int     15h             ; Request 16:16 connect.
        jnc     apm2
        DBPRT   17,1,<ax=$x\n>,<ax>
        jmp     short apm2
apm1:
ifndef  HYBERI13
        test    _hyberInfoHdr.hih_hyberflags, HF_APM32
else   ;HYBERI13
        test    es:hyberInfoHdr_s.hih_hyberflags[di], HF_APM32
endif  ;HYBERI13
        jz      apm2
        DBPRT   17,1,<connect apm 32\n>
        mov     ax, 5303h
        xor     bx, bx
        int     15h             ; Request 16:32 connect.
        jnc     apm2
        DBPRT   17,1,<ax=$x\n>,<ax>
apm2:

ifndef HYBERI13
        ; if there is a cd rom drive we need to cause it to generate some
        ; status so that the SurePath bios RapidResume code will not hang
        mov     ax, HD_DRV0
        mov     dx, HD_C1 + HD_DHR
        out     dx, ax
        DELAY
        mov     ax, HD_NOP
        mov     dx, HD_C1 + HD_CR
        out     dx, ax
        DELAY
        mov     ax, HD_DRV1
        mov     dx, HD_C1 + HD_DHR
        out     dx, ax
        DELAY
        mov     ax, HD_NOP
        mov     dx, HD_C1 + HD_CR
        out     dx, ax
endif ;HYBERI13

if HYBERDEBUG_R EQ 0
        ; put the video adapter into a graphics mode which should avoid the
        ; clutter we see when the hybernat driver exits into pm.
        ; Note:  Must do graphic mode first before text mode.
        ;        This order must be reserved!!!!
        mov     ah, 0           ; Set and clear display graphic mode
        mov     al, 12h
        int     10h
        mov     ah, 0           ; Set and clear display text mode
        mov     al, 03h         ; Note:  This step is required for some
        int     10h             ;        Aptiva systems.

endif ;HYBERDEBUG_R EQ 0
        push    si
        mov     si, offset restoreMsg
        call    ErrPrt
        pop     si

        ; init the PICs
        lea     si, PIC1Init                    ; send init commands to PICs
        call    InitPIC
        lea     si, PIC2Init
        call    InitPIC
        mov     al,OCW2_SET_PRIORITY + 2        ; make IRQ 2 lowest priority
        out     PIC1_PORT0,al

ifndef HYBERI13
        ; set up protect arguments
        xor     eax, eax
        mov     ax, _hyberInfoHdr.hih_dataloc   ; phys loc of InfoHdr & data
        shl     eax, PAGESHIFT
        mov     edi, eax                        ; pass it in edi
        xor     eax, eax
        mov     ax, _hyberInfoHdr.hih_hldrloc   ; phys loc of hyberldr
        shl     eax, PAGESHIFT
        mov     esi, eax

        ; build jmp far for protect entry
        xor     eax, eax
        mov     ax, cs
        shl     eax, PARASHIFT

        add     eax, offset main32                      ; for pm entry
        mov     goProtectAddr, eax
        mov     ax, _hyberInfoHdr.hih_cs
        mov     goProtectSel, ax

        mov     ax, _hyberInfoHdr.hih_GDTFrame
        shl     ax, (PAGESHIFT-PARASHIFT)
        mov     gs, ax                                  ; gs points to GDT base

        ; set up temporary gdt entries for protect mode entry
        xor     edx, edx
        mov     dx, _hyberInfoHdr.hih_cs
        DBPRT   17,1,<cs=$x\n>,<dx>
        mov     eax, 0000ffffh
        mov     gs:[edx], eax                           ; code
        mov     eax, 00cf9b00h
        mov     gs:[edx+4], eax
        mov     dx, _hyberInfoHdr.hih_ds
        DBPRT   17,1,<ds=$x\n>,<dx>
        mov     eax, 0000ffffh
        mov     gs:[edx], eax                           ; data
        mov     eax, 00cf9300h
        mov     gs:[edx+4], eax
        ; note: dx is used after protect mode entry

        ; set gdt
        xor     eax, eax
        mov     ax, gs
        shl     eax, PARASHIFT
        mov     ProtGDT2, eax                           ; phys addr of GDT
        mov     ax, _hyberInfoHdr.hih_gdt
        mov     ProtGDT, ax                             ; limit
        DBPRT   17,1,<going protected\n>
        cli
        lgdt    fword ptr ProtGDT                       ; kernel gdt (phys addr)

        ; here goes
        smsw    ax
        or      ax, 1
        lmsw    ax
        jmp     qflsh
qflsh:
        ; dx has new selector value
        mov     ds, dx
        mov     es, dx
        mov     ss, dx
;       jmp     far ptr main32
        db      66h                                     ; operand32
        db      0eah
goProtectAddr   dd      0
goProtectSel    dw      0
else   ;HYBERI13
        DBPRT   17,1,<calling unhyber\n>
        pop     di                      ; restore di ;; 155895
        mov     ax, ComPortAddr         ; pass on the debugger port address
        mov     es:hyberInfoHdr_s.hih_debugport[di],ax
        call    unhyber
endif  ;HYBERI13

main2:
        call    IndicateAltF1

ifdef   TIMELOAD
        DBPRT   17,1,<Load Krnl\n>
        call    PrintTime
endif
        DBPRT   17,1,<cldr\n>
        push    twodiskboot                     ; flag if disk swap needed
        mov     ax, ldrend
        push    ax
        push    cs
        push    offset DGROUP:_TEXT             ; Pass length of DosHlp
        call    _loader
;       add     sp,8                            ; clean up
; leave twodiskboot on stack -
; _loader parms changed
        add     sp,6                            ; clean up

ifdef SAVEDOSIVT_OLD    ;; ***********160195 BEGIN *************
;; This is the old way to save BIOS constructed IVT.
;; This code does not save Extended BIOS Data Area (XBDA)
;; Remove this code will eliminate 1280 bytes from the size
;; of the os2ldr
        ; save the original bios constructed ivt
        SaveReg         <ds, es, si, di, cx>
        push    0
        pop     ds                              ; point to ivt
        mov     si, 0
        push    DOSHLP_CODESEL
        pop     es
        mov     di, offset DosIVT
        mov     cx, DosIVTSize/2
        cld
        rep     movsw
        RestoreReg      <cx, di, si, es, ds>
else
;; This is the new way to save IVT, BIOS Data Area,
;; Extended BIOS Data Area (XBDA)

        SaveReg         <ds, es, ax, bx, cx, di, si>

        mov     ax, _BIOSSeg          ; ax = BIOSSeg

        push    0
        pop     ds                    ; ds = 0 segment
        xor     si, si
        mov     es, ax                ; es = segment to save BIOS
        xor     di, di
        mov     cx, 0500h/2           ; Size of IVT & BIOS data
        cld
        rep     movsw                 ; Source ds:[si] target es:[di]

        mov     bx, ds:[040eh]        ; bx = original XBDA segment
        cmp     bx, 0                 ; XBDA exists?
        je      no_XBDA               ;   NO, skip
        mov     es, bx
        mov     cl, es:[0]            ; Get size of XBDA in 1K bytes
        xor     ch, ch                ; Shift left 10 bits to get num of bytes
        shl     cx, 9                 ;    but need to shift right 1 bit for
                                      ;    movsw operations
        mov     es, ax                ; es=segment to save BIOS
        mov     ds, bx                ; ds=original XBDA segment
        xor     si, si                ; si=0
        rep     movsw                 ; source ds:[si] target es:[di]

no_XBDA:
        mov     di,DOSHLP_CODESEL
        mov     ds, di
        assume  ds:DosHlpData
        mov     SavedBIOSSeg, ax      ; save for doshlp code

        RestoreReg      <si, di, cx, bx, ax, es, ds>
        assume  ds:_DATA
endif;; ********************* 160195 END *****************

        ; Now that all of the INT 15H calls have been made to
        ; copy code into high memory, the A20 line can be enabled
        ; without worrying if it will be disable again

        DBPRT   17,1,<a20\n>
        call    DHEnableA20

        DBPRT   17,1,<pDHt\n>
        call    PatchDosHlpTable

        ; change base vectors for pic 1

        DBPRT   17,1,<rPIC\n>
        call    dhRemapPIC

        mov     bx,offset _MsInitVars
        mov     [bx].mi_pArenaInfo.Segmt,ds     ; set ArenaInfo pointer
        mov     ax, ldrend
        mov     [bx].mi_pArenaInfo.Offst, ax
        mov     [bx].mi_pbpb.Segmt,ds           ; set boot BPB pointer
        mov     [bx].mi_pbpb.Offst,offset BootBPB
        mov     al,[BootDrive]                  ; set boot drive
        mov     [bx].mi_Drive,al
        mov     al,[_BootFlag]                  ; set boot flag
        mov     [bx].mi_BootFlags,al
        mov     [bx].mi_DosHlpSeg,DOSHLP_CODESEL; set DosHlp code seg/sel
        mov     [bx].mi_DosHlpLen,offset _TEXT  ; set DosHlp length
        mov     [bx].mi_DosHlpAddr.Segmt,cs     ; set pointer to DosHlp Table
        mov     [bx].mi_DosHlpAddr.Offst,offset DosHlpFunctions
        mov     [bx].mi_OEMDDStra, offset DGROUP:OEMHLP_STRATEGY; pass OEMHLP strategy to kernel

ifdef 0
        mov     al,[_BreakChar]         ; store kernel break char
        mov     [bx].mi_BreakChar,al
        push    bx                      ; save MsInitVars pointer
        push    bx                      ; pass MsInitVars pointer
        call    _dumpmsvars
        pop     bx                      ; clean off stack
        pop     bx                      ; restore MsInitVars pointer

    ifdef CALLDEBUGGER
        inc     [indebugger]
        call    Debug_Entry
        cmp     [_StackFence],STACKMAGIC
        dec     [indebugger]
    endif
endif

ifdef   TIMELOAD
        DBPRT   17,1,<Jmp Krnl\n>
        call    PrintTime
endif

        pop     cx                      ; get twodiskboot flag

        DBPRT   -1,1,<j syi\n>
        call    [_OS2Init]              ; (SS:BX) points to MsInit structure.
        DBPRT   -1,1,<syi2\n>

        jmp     _fatal

EndProc main

if HYBERDEBUG_R GE 2
;------------------------------------------------------------------------------
; _dispRegs - display registers using DBPRT
;   Entry:  registers at entry displayed, the return address is used for ip.
;   Exit:   all registers preserved
;------------------------------------------------------------------------------
Procedure _dispRegs
    assume ds:NOTHING
        push    eax
        pushf
        pushf
        push    ax
        shr     eax, 16
        DBPRT   17,1,<eax=$x>,<ax>
        pop     ax
        DBPRT   17,1,<$x>,<ax>
        mov     eax, ebx
        shr     eax, 16
        DBPRT   17,1,< ebx=$x$x>,<ax,bx>
        mov     eax, ecx
        shr     eax, 16
        DBPRT   17,1,< ecx=$x$x>,<ax,cx>
        mov     eax, edx
        shr     eax, 16
        DBPRT   17,1,< edx=$x$x>,<ax,dx>
        mov     eax, esi
        shr     eax, 16
        DBPRT   17,1,< esi=$x$x>,<ax,si>
        mov     eax, edi
        shr     eax, 16
        DBPRT   17,1,< edi=$x$x\n>,<ax,di>
        push    bp
        mov     bp,sp
        mov     ax,ss:[bp+8]        ; pick up ret addr
        DBPRT   17,1,<     ip=$x>,<ax>
        pop     bp
        mov     eax, esp
        shr     eax, 16
        DBPRT   17,1,< esp=$x$x>,<ax,sp>
        mov     eax, ebp
        shr     eax, 16
        DBPRT   17,1,< ebp=$x$x>,<ax,bp>
        pop     ax
        DBPRT   17,1,< flags=$x\n>,<ax>
        DBPRT   17,1,<cs=$x ss=$x ds=$x es=$x fs=$x gs=$x\n>,<cs,ss,ds,es,fs,gs>
        popf
        pop     eax
        ret
EndProc _dispRegs

ifdef HYBERI13
;------------------------------------------------------------------------------
; _dispStg - display storage using DBPRT
;   Entry:  ds:si = address, cx = byte count
;   Exit:   si, cx changed, all other regs preserved
;------------------------------------------------------------------------------
Procedure _dispStg
        assume ds:NOTHING
        push    ax
        pushf
        add     cx,15              ; convert cx from bytes to line count
        shr     cx,4
dispstglineloop:
        push    cx
        DBPRT   17,1,<$x:$x  >,<ds,si>
        mov     cx,8
        push    si
dispstgwordloop:
        lodsw
        DBPRT   17,1,< $x>,<ax>
        loop    dispstgwordloop
        DBPRT   17,1,<   *>
        mov     cx,16
        pop     si
dispstgcharloop:
        lodsb
        cmp     al,20h
        jge     dispstgnotlo
        mov     al,'.'
dispstgnotlo:
        cmp     al,7Eh
        jle     dispstgnothi
        mov     al,'.'
dispstgnothi:
        DBPRT   17,1,<$c>,<ax>
        loop    dispstgcharloop
        DBPRT   17,1,<*\n>
        pop     cx
        loop    dispstglineloop
        popf
        pop     ax
        ret
EndProc _dispStg
endif ;HYBERI13

endif ;HYBERDEBUG_R GE 2

ifdef   TIMELOAD
;***    PrintTime
;
;       This function prints the current time (and CrLf)
;
;       ENTRY   none
;       EXIT    none
;       USES    none

Procedure PrintTime
        push  eax
        push  DS

        mov   ax,40h
        mov   ds,ax

        mov   eax,dword ptr ds:[6Ch]

        out_dword
        out_crlf

        pop   DS
        pop   eax
        ret
EndProc PrintTime
endif



;***    UpdateBelmontCMOS
;
;       This function checks if this machine is a Belmont
;       and if so sets bits in the CMOS which instruct the
;       machine to boot from the hard disk instead of booting
;       DOS from ROM (it's default).
;
;       The model byte for the Belmont is f8 hex (same as 80386)
;       The submodel byte is 30 hex (unique)
;
;       ENTRY   DS = DGROUP
;       EXIT    none
;       USES    AX, CX, Flags

Procedure UpdateBelmontCMOS
        cmp    MachModelSubModel,0f830h ; Check if Belmont
        jnz    short ubc1           ; Jmp if no !
        mov    ax, 2301h            ; BIOS function 23, subfunction 1 (write)
        mov    cx, 0200h            ; set bit to boot from hard file
        int    15h                  ; call BIOS
ubc1:
        ret
EndProc UpdateBelmontCMOS




;***    IODELAYCalibrate
;
;       Determine the number of loops necessary to provide a
;       a minimum of ~500ns delay.
;
;       PSEUDOCODE
;
;           Program timer 0 to use mode 2 (rate generator) with
;               maximum counter value
;           Snapshot counter{0} (snapshot adjustment)
;           Snapshot counter{1} (start of loop)
;           Perform fixed loop with (fixed loop count)
;           Snapshot counter{2} (end of loop)
;           totalcount=counter{2}-(counter{1}-counter{0})
;           IODelayCnt=totalcount*(8/5)/(fixed loop count)
;
;       NOTES
;
;           The snapshot image is as follows:
;
;                               ---------------
;               counter{0} -->  | Snapshot    |<----- Snapshot
;                               ---------------  /
;               counter{1} -->  | Snapshot    |<---|
;                               ---------------    |
;                               | Loop        |    -- Loop + Snapshot
;                               ---------------    |
;               counter{2} -->  | Snapshot    |<---|
;                               ---------------
;
;           Since the difference between counter{2} and counter{1}
;           includes the time taken by the actual snapshot, we take
;           a third counter value (counter{0}) to make the adjustment
;           for the snapshot time.  This will give us a more accurate
;           value for the amount counter ticks spent in the loop
;           itself.

Procedure IODELAYCalibrate

        pusha
        push    ds

        push    cs
        pop     ds
    assume ds:DGROUP

        pushf
        cli                             ; we can't have interrupts occur

        ; Set the timer to the largest count value

        mov     al,SC_CNT0+RW_LSBMSB+CM_MODE2
        out     PORT_CW,al              ;program count mode of timer 0
        call    EarlyDelay
        xor     al,al
        out     PORT_CNT0,al            ;program timer 0 count
        call    EarlyDelay              ;  count == 0h actually means 10000h
        out     PORT_CNT0,al
        call    EarlyDelay

        ; Snapshot value

        mov     al,(SC_CNT0 OR RW_LATCHCNT)     ;Latch PIT Ctr 0 command.
        out     PORT_CW,al
        call    EarlyDelay
        in      al,PORT_CNT0            ;Read PIT Ctr 0, LSByte.
        call    EarlyDelay
        mov     bl,al
        in      al,PORT_CNT0            ;Read PIT Ctr 0, MSByte.
        mov     bh,al

        ; Snapshot value - use for error correction

        mov     al,(SC_CNT0 OR RW_LATCHCNT)     ;Latch PIT Ctr 0 command.
        out     PORT_CW,al
        call    EarlyDelay
        in      al,PORT_CNT0            ;Read PIT Ctr 0, LSByte.
        call    EarlyDelay
        mov     cl,al
        in      al,PORT_CNT0            ;Read PIT Ctr 0, MSByte.
        mov     ch,al

        ; Perform Loop
        mov     si,1000h                 ; initialize loop counter
        ALIGN 4
idc10:  dec     si
        jnz short idc10

        ; Snapshot value

        mov     al,(SC_CNT0 OR RW_LATCHCNT)     ;Latch PIT Ctr 0 command.
        out     PORT_CW,al
        call    EarlyDelay
        in      al,PORT_CNT0            ;Read PIT Ctr 0, LSByte.
        call    EarlyDelay
        mov     dl,al
        in      al,PORT_CNT0            ;Read PIT Ctr 0, MSByte.
        mov     dh,al

        popf

        ; Calculate required loop count for ~ 838524 ns

        sub     bx,cx                   ;bx - snapshot time
        sub     cx,dx                   ;cx - loop consumption + snapshot time
        sub     cx,bx
        xchg    cx,dx                   ;dx - acutal loop consumption

        ; dx - actual loop consumption in multiples of ~ 838 ns
        ;
        ;   We need the loop consumption in multiples of ~ 500 ns, it
        ;   would be just as easy to use 8/5 as the fraction, but
        ;   the end result is ~ 524 ns. To make the loop consumption
        ;   closer to ~ 500 ns, we are using 40/24 as the fraction.

        mov     ax,40                   ; 40*count
        imul    dx                      ; result in dx:ax
        mov     bx,24                   ; 40/24 count
        idiv    bx                      ; result in ax

        ; ax - actual loop consumption in multiples of ~ 500 ns

        xor     dx,dx                   ; remove remainder
        mov     bx,1000h                ; Number of loops
        xchg    bx,ax                   ; need loops/loop consumption
        idiv    bx                      ; ax - number of loops needed
                                        ; for ~ 524 ns
        or      dx,dx                   ; is there a remainder
        jz      short idc20             ; no - no round needed
        inc     ax

idc20:
        inc     ax                      ; make the count one greater than
                                        ; value computed
        mov     _IODelayCnt,ax          ; save value for later use

        pop     ds
        popa

        ret

EndProc IODELAYCalibrate




;***    _fcopy - copy cnt bytes forward from src to dst
;       0 implies 64k bytes

Procedure _fcopy
    assume ds:DGROUP,es:nothing,ss:DGROUP
    ArgVar fcp_src,dword
    ArgVar fcp_dst,dword
    ArgVar fcp_cnt,word
        EnterProc
        push    si
        push    di
        push    ds

        lds     si,[fcp_src]
    assume ds:nothing
        les     di,[fcp_dst]
    assume es:nothing
        mov     cx,[fcp_cnt]
        cld

        mov     ax, cx          ;Save count of bytes
        or      cx, cx          ;If zero, then do 64K
        jz      fcpy_64K

        shr     cx, 2           ; Number of DWORDS to copy
fcpy_entry:
        rep     movsd
        xchg    ax, cx          ;Get number of bytes remaining
        and     cx, 3           ; (low order 3 bits in AX)
        rep     movsb

        pop     ds
    assume ds:DGROUP
        pop     di
        pop     si
        LeaveProc
        ret

fcpy_64K:
        mov     cx,4000h         ;64K/4 = 4000h DWORDs to copy
        xor     ax,ax            ;zero byte remainder to copy
        jmp     fcpy_entry       ;Jump back to do copy

EndProc _fcopy




;***    _bcopy - copy cnt bytes backwards from src to dst
;       src points to the base of the source block
;       dst points to the base of the destination block
;       0 implies 64k bytes

Procedure _bcopy
    assume ds:DGROUP,es:nothing,ss:DGROUP
    ArgVar bcp_src,dword
    ArgVar bcp_dst,dword
    ArgVar bcp_cnt,word
        EnterProc
        push    si
        push    di
        push    ds

        lds     si,[bcp_src]
    assume ds:nothing
        les     di,[bcp_dst]
    assume es:nothing
        mov     cx,[bcp_cnt]
        add     si,cx
        add     di,cx
        dec     si
        dec     di
        std
        jcxz    short bcp20
        shr     cx,1
        jnc     short bcp30
        movsb
        jcxz    short bcp90
        jmp     short bcp30

bcp20:  mov     cx,8000h
bcp30:  dec     si
        dec     di
        rep movsw
bcp90:  cld

        pop     ds
    assume ds:DGROUP
        pop     di
        pop     si
        LeaveProc
        ret
EndProc _bcopy


;********************* START OF SPECIFICATIONS *********************
;
; SUBROUTINE NAME: _MoveHigh
;
; DESCRIPTIVE NAME: Move data to high memory
;
; FUNCTION:      Move the data from the source address to the high
;                memory.
;
; ENTRY POINT:  _MoveHigh
;    LINKAGE:   call (near) from loader
;
; INPUT:        SS:SP -> the return address
;               SP+2 -> source physical address
;               SP+6 -> destination physical address
;               SP+A -> byte count
;
; EXIT-NORMAL: return to loader
;
; EXIT-ERROR:    None
;
; EFFECTS:       The segments within OS2KRNL are loaded into memory
;
; MESSAGES:
;
; INTERNAL REFERENCES:
;    ROUTINES:
;
; EXTERNAL REFERENCES:
;    ROUTINES:  int 15H
;
; PSEUDO CODE:
;
; get source address from stack
; convert source address to 24 bit address
; get target address from stack
; get byte count from stack
; set segment limit = byte count + 1
; issue int 15h to do the block move
; move source data to high memory

Procedure _MoveHigh
    assume ds:DGROUP,es:nothing,ss:DGROUP
    ArgVar mvh_src,dword
    ArgVar mvh_dst,dword
    ArgVar mvh_cnt,word
        EnterProc
        push    si
        push    di

        mov     si,offset blockmovegdt
        mov     bx,si
        mov     cx,GDTSIZE
mvh00:  mov     [bx].d_access,D_DATA0
        add     bx,size desctab
        loop    short mvh00

        mov     ax,[mvh_src].loword     ; set source paddr low word
        mov     [sourcedesc].d_loaddr,ax
        mov     ax,[mvh_src].hiword     ; set source paddr high word
        mov     [sourcedesc].d_hiaddr,al
        mov     [sourcedesc].d_extaddr,ah

        mov     ax,[mvh_dst].loword     ; set target paddr low word
        mov     [targetdesc].d_loaddr,ax
        mov     ax,[mvh_dst].hiword     ; set target paddr high word
        mov     [targetdesc].d_hiaddr,al
        mov     [targetdesc].d_extaddr,ah

;       set segment limit = byte count - 1 (then rounded up)

        mov     cx,[mvh_cnt]            ; get size
        dec     cx                      ; convert to limit
        or      cx,1                    ; make sure size is even
        mov     [targetdesc].d_limit,cx
        mov     [sourcedesc].d_limit,cx
        inc     cx                      ; convert back to size
        jcxz    short mvh50             ; jump if size is 64k
        shr     cx,1                    ; convert byte count to word count
        jmp     short mvh60

mvh50:  mov     cx,8000h                ; copy 32k words
mvh60:  mov     ah,87h

        push    ds
        pop     es
        pushf              ; int 15/87 is not documented to preserve flags
                           ; ... most machines seem to, but Aox does not
    assume es:nothing
        int     15h                     ; do the block copy

        popf
        pop     di
        pop     si
        LeaveProc
        ret
EndProc _MoveHigh




;***    _clearobj - clear cnt bytes at dst
;       0 clears 0 bytes

Procedure _clearobj
    assume ds:DGROUP,es:nothing,ss:DGROUP
    ArgVar clo_dst,dword
    ArgVar clo_cnt,word
        EnterProc
        push    di

        les     di,[clo_dst]
    assume es:nothing
        mov     cx,[clo_cnt]
        cld
        jcxz    short clo90
        xor     ax,ax
        shr     cx,1
        jnc     short clo80
        stosb
clo80:  rep stosw
clo90:  pop     di
        LeaveProc
        ret
EndProc _clearobj




;***    _resetsegs(newseg)
;
;       Reset cs,ds,ss to the passed parameter.
;
Procedure _resetsegs
    assume ds:DGROUP,es:nothing,ss:DGROUP
        pop     cx      ; return offset
        pop     bx      ; new cs value
        mov     ds,bx
        assume ds:DGROUP
        mov     ss,bx
        assume ss:DGROUP
        push    bx      ; set up for far return
        mov     bx,offset rss80
        push    bx
        retf

rss80:  push    bx      ; re-push (garbage) parameter for C cleanup
        push    cx      ; re-push near return address
        ret
EndProc _resetsegs




;***    _memcpyf - copy memory
;
;       This function is a C-compatible function that
;       block-copies memory.  It assumes that if
;       src and dst are in different segments, then
;       the segments do not overlap.
;
;       USAGE   push    word ptr nbytes
;               push    dword ptr src
;               push    dword ptr dst
;               call    _memcpyf
;               add     sp,10
;       EXIT    none
;       USES    AX, CX, ES, Flags

Procedure _memcpyf
    assume ds:DGROUP,es:nothing,ss:DGROUP
    ArgVar mcf_src,dword
    ArgVar mcf_dst,dword
    ArgVar mcf_cnt,word
        EnterProc
        push    di
        push    si
        push    ds
        lds     si,[mcf_src]            ; DS:SI points to source
        assume ds:nothing
        les     di,[mcf_dst]            ; ES:DI points to destination
        assume es:nothing
        mov     cx,[mcf_cnt]            ; CX contains the number of bytes
        mov     ax,ds                   ; Source, destination in same segment?
        cmp     ax,[mcf_dst].Segmt
        jne     short mcpy0             ;  no, skip ahead
        cmp     di,si                   ; Destination precedes source?
        je      short mcpy1             ;  no, they're the same, do nothing
        jb      short mcpy0             ;  yes, skip ahead
        add     di,cx                   ; Point to ends of buffers
        dec     di
        add     si,cx
        dec     si
        std                             ; Autodecrement
mcpy0:  rep movsb                       ; Copy those bytes!
        cld                             ; Make sure direction flag clear
mcpy1:  pop     ds
        assume ds:DGROUP
        pop     si
        pop     di
        LeaveProc
        ret
EndProc _memcpyf




;***    _memsetf - initialize memory
;
;       This function is a C-compatible function that
;       initializes the specified block of memory.
;
;       USAGE   push    word ptr nbytes
;               push    word ptr value
;               push    dword ptr memaddr
;               call    _memsetf
;               add     sp,8
;       EXIT    none
;       USES    AX, CX, ES, Flags

Procedure _memsetf
    assume ds:DGROUP,es:nothing,ss:DGROUP
    ArgVar msf_dst,dword
    ArgVar msf_value,word
    ArgVar msf_cnt,word
        EnterProc
        push    di
        les     di,[msf_dst]
    assume es:nothing
        mov     ax,[msf_value]
        mov     cx,[msf_cnt]
        cld
        rep stosb
        pop     di
        LeaveProc
        ret
EndProc _memsetf




;**     PatchDosHlpTable -- fix up entries in DosHlp Function Table
;
;       This routine fixes up the entries for discardable DosHlps that
;       are called in real mode, so that the entries have the proper
;       segment value.
;
;       Entry:
;               ds -> _DATA
;
;       Exit:
;               the DosHlpFunctionTable has been fixed up.
;
;       Uses:
;               none

        assume cs:DGROUP, ds:DGROUP, ss:DGROUP
Procedure PatchDosHlpTable, near

        SaveReg <ax, bx>

        ; set up for patching
        mov     bx, offset DosHlpFunctions
        mov     ax, _SysInitSeg

        ; patch entries
        mov     word ptr [bx].DosHlpInit + 2, ax
        mov     word ptr [bx].DosHlpSizeMem + 2, ax
        mov     word ptr [bx].DosHlpConfig + 2, ax
        mov     word ptr [bx].DosHlpGetDriveParms + 2, ax
        mov     word ptr [bx].DosHlpReadSectors + 2, ax
        mov     word ptr [bx].DosHlpRealGetMessage + 2, ax

        RestoreReg <bx, ax>
        ret

EndProc PatchDosHlpTable




ifdef PMDEBUG

if 0

;***    _debug - call the debugger if enabled
;
;       ENTRY   NONE
;       EXIT    NONE
;       USES    NONE

Procedure _debug
    assume ds:nothing,es:nothing,ss:nothing
ifdef CALLDEBUGGER
        inc     [indebugger]
        push    ax
        mov     ax,offset _dbflag
        call    Debug_Entry
        pop     ax
        dec     [indebugger]
endif
        ret
EndProc _debug




;***    _dbcheck - check for dbflag change/debug entry requests
;
;       ENTRY   NONE
;       EXIT    NONE
;       USES    AX, Flags

Procedure _dbcheck
    assume ds:DGROUP,es:nothing,ss:DGROUP
        call    inchar                  ; handle dbflag change requests
        jz      short dbcx              ; jump if no character ready
        cmp     al,'C'-40h              ; is it a CTL-C?
        jz      short _debug
dbcx:   ret
EndProc _dbcheck

endif




;***    DPRINTF _ Debug Printf
;
;       Dprintf is a kernel debug print formatting package.  It is intended
;       to produce conviently formatted output.
;
;       Dprintf is called, indirectly, by a macro:
;
;       DBPRT   n,m,<string>,<a1,...,an>
;
;       string = format string
;       a1     = first argument
;       an     = last argument
;
;       The format string is an ASCIZ string which can contain 2 types of
;       specifications:  data-format specifications and literal characters.
;       Data format specifications always begin with a '$' character; all
;       characters not part of a data format specification are treated as
;       literal characters.
;
;       Literal characters
;           - any character not part of a format specification.  Special
;             non-printing characters are:
;               \n      - CRLF
;               \t      - tab
;               \b      - bell
;               \\      - \
;               \$      - $
;
;       Format Specifications
;
;       A format specification takes the form:
;            $ [@] <char>
;
;       where <char> =
;
;           x       - print argument as a hex word
;           d       - print argument as decimal word
;           c       - print argument as ascii character
;           b       - print argument as hex byte
;               For each of the above formats, the supplied argument
;               is a 16-bit word - the value to be printed.  The optional @
;               (described below) allows a segmented address to be supplied,
;               instead.
;
;           s[nn]   - print argument as asciz string; if optional decimal
;                     argument follows the format character this specifys
;                     a maximum string length.  Non printing characters are
;                     printed in the form \nnn where "nnn" is the octal byte
;                     value.
;                     Note that this format character cannot be directly
;                     followed by a digit unless that digit is to be taken
;                     as the start of a length argument.
;
;           Bnn     - print argument as hex bytes.  The required following
;                     decimal argument is the number of bytes to print.
;
;               Both of these formats take a long address as their argument.
;               The '@' character is thus invalid for these formats.
;
;       WARNINGS
;           As befitting a debug routine, DPRINTF does not have a whole lot
;           of "failsafe" code in it. Specifically:
;               The @ argument must NOT be specified with the 's' or 'B'
;                    format
;               A string/byte-length argument of 0 is taken as 65536
;           The string "%% BAD FMT %%" appears in the output when
;               1) an illegal format specifier is given, or
;               2) the B format is given a 0 or missing length
;
;       ENTRY   (SP+n)   = address of format string
;               (SP+n-2) = first argument word
;               (SP+n-4) = second argument word
;                   .
;               (SP+2)   = last argument word
;               (SP)     = offset of return address
;               (AX)     = offset of format string on the stack
;       EXIT    NONE
;       USES    Flags

Procedure DPRINTF
    assume ds:nothing,es:nothing,ss:DGROUP
        push    ds
        push    es
        push    bp
        push    di
        push    si
        push    dx
        push    cx
        push    bx
        push    ax              ; save registers
        cld

        mov     bp,ax
        mov     si,[bp]         ; get address of format string
        dec     bp
        dec     bp
        push    cs
        pop     ds
    assume ds:DGROUP

;       Scan format string for next character
;
;       (ds:si) = address of format string
;       (ss:bp) = address of next argument

dpf1:   lodsb                   ; (al) = format string byte
        and     al,al
        je      short dpf3      ; all done
        cmp     al,'$'
        je      short dpf4      ; is data escape
        cmp     al,'\'
        jnz     short dpf2      ; got the character

;       it's an "\" escape code - crack the argument character

        lodsb
        and     al,al
        je      short dpf3      ; all done, ignore hanging backslash
        xchg    ah,al
        mov     al,0Ch
        cmp     ah,'n'
        jne     short dpf1$5    ; not \n
        mov     al,0dH
        call    putchar
        mov     al,0aH
        jmp     short dpf2      ; print LF

dpf1$5: cmp     ah,'t'
        mov     al,9
        je      short dpf2      ; is \t
        cmp     ah,'b'
        mov     al,7
        je      short dpf2      ; is \b
        xchg    ah,al
dpf2:   call    putchar
        jmp     short dpf1

;       have the end of the format string - exit

dpf3:   pop     ax
        pop     bx
        pop     cx
        pop     dx
        pop     si
        pop     di
        pop     bp
        pop     es
    assume es:nothing
        pop     ds
    assume ds:nothing
        ret

;***    Have a '$' character - is data format escape
;
;       Get address of data into es:di
;
;       (bp) = address of data value

dpf4:   mov     di,bp
        push    ss
        pop     es              ; (es:di) = address of data value
    assume es:DGROUP
        dec     bp              ; point to next argument
        dec     bp
        lodsb                   ; (al) = format specifier
        cmp     al,'@'
        jne     short dpf5      ; not an indirect flag
        les     di,[bp]
        dec     bp              ; have an extra 2 for @
        dec     bp
        lodsb
dpf5:   cmp     al,'x'
        jne     short dpfd1     ; not 'x'

;       is 'x' format - print hex word

        mov     ax,es:[di]
        call    thw             ; type hex word
        jmp     short dpf1

dpfd1:  cmp     al,'d'
        jnz     short dpfc1     ; not 'd'

;       is 'd' format - print decimal word

        mov     ax,es:[di]
        call    tdw             ; type decimal word
        jmp     short dpf1

dpfc1:  cmp     al,'c'
        jne     short dpfb1

;       is 'c' format - print character

        mov     al,es:[di]
        call    putchar
        jmp     short dpf1

dpfb1:  cmp     al,'b'
        jne     short dpfs1

;       is 'b' format - print hex byte

        mov     al,es:[di]
        call    thb             ; type hex byte
        jmp     dpf1

dpfs1:  cmp     al,'s'
        jne     short dpfbb1

;       is 's' format - print ASCIZ string.  First, check for
;               optional decimal limit

SSB:    sub     cx,cx           ; set 65536 limit
        les     di,[bp]         ; (es:DI) = fwa of string
        dec     bp              ; argument to 's' was two words
        dec     bp
        mov     al,[si]
        cmp     al,'0'
        jb      short dpfs2     ; not decimal
        cmp     al,'9'
        ja      short dpfs2     ; not decimal
        call    atod            ; (ax) = decimal value, (ds:si) updated
        xchg    cx,ax

;       print asciz string at es:di, max of (cx) characters
;       (cx) = 0  means max of 65536
;
;       Other sections of code in dpf jump here to print strings

dpfs2:  mov     al,es:[di]
        inc     di
        and     al,al
        je      short dpfs3
        call    putchar
        loop    dpfs2           ; continue if not at limit
dpfs3:  jmp     dpf1

dpfbb1: cmp     al,'B'
        je      short dpfbb2    ; is 'B' format

;       error in format code - print message

dpferr: push    cs
        pop     es
    assume es:DGROUP
        mov     di,OFFSET dpfa  ; (es:di) = error message
        sub     cx,cx
        jmp     short dpfs2

dpfa:   db      '%% BAD FMT %%',0

;       have B format

dpfbb2: call    atod            ; (ax) = length specifier
        jc      short dpferr    ; number not there - error
        xchg    cx,ax
        jcxz    short dpferr    ; number is 0 - error
        les     di,[bp]         ; (es:DI) = fwa of string
    assume es:nothing
        dec     bp              ; argument to 's' was two words
        dec     bp
dpfbb3: mov     al,es:[di]
        call    thb             ; type hex byte
        mov     al,' '
        call    putchar         ; space em out
        inc     di
        loop    dpfbb3          ; do em all
        jmp     dpf1
EndProc DPRINTF




;***    thb - Type Hex Byte
;
;       thb types a hex byte (via "putchar")
;
;       ENTRY   (AL) = byte
;       EXIT    none
;       USES    ax, bx, flags

_DATA   segment
THBA    db      '0123456789abcdef'
_DATA   ends

Procedure thb,,local
    assume ds:nothing,es:nothing,ss:DGROUP
        push    ax
        shr     al,1
        shr     al,1
        shr     al,1
        shr     al,1
        and     ax,0fH
        xchg    bx,ax
        mov     bl,THBA[bx]
        xchg    ax,bx
        call    putchar             ; put first character
        pop     ax
        and     ax,0fH
        xchg    bx,ax
        mov     bl,THBA[bx]
        xchg    ax,bx
        call    putchar
        ret
EndProc thb




;***    thw - Type Hex Word
;
;       thw types a word in hex (via "putchar")
;
;       ENTRY   (AX) = word
;       EXIT    none
;       USES    AX, flags

Procedure thw,,local
    assume ds:nothing,es:nothing,ss:DGROUP
        push    ax
        xchg    ah,al
        call    thb
        pop     ax
        call    thb
        ret
EndProc thw




;***    tdw - Type Decimal Word
;
;       tdw types (via "putchar") the unsigned decimal representation
;       of a 16-bit unsigned integer.  Only significant digits are
;       printed; if the number is 0 a "0" is printed.
;
;       ENTRY   (AX) = number
;       EXIT    none
;       USES    AX, flags

Procedure tdw,,local
    assume ds:nothing,es:nothing,ss:DGROUP
        push    cx              ; preserve registers
        push    dx
        mov     cx,10
        call    tdwcrack                ; recurse cracking digits
        pop     dx
        pop     cx
        ret
EndProc tdw




;***    tdwcrack - crack number recursively
;
;       tdwcrack cracks the least significant decimal digit.  If there
;       are no higher-significant digits, print and return.
;       else, recurse for higher digits
;
;       (AX) = value
;       (CX) = 10

Procedure tdwcrack,,local
    assume ds:nothing,es:nothing,ss:DGROUP
        sub     dx,dx
        div     cx              ; (ax) = quotient, (dx) = remainder
        and     ax,ax
        jz      short tdwcrack1 ; this is highest-order, do it
        push    dx
        call    tdwcrack
        pop     dx
tdwcrack1:      xchg    ax,dx
        add     al,'0'
        call    putchar
        ret
EndProc tdwcrack




;***    atod - Convert ASCII string to decimal number
;
;       atod is called to convert an ascii string of digits to a
;       decimal number.  Digits are converted until we run out of them.
;
;       ENTRY   (DS:SI) = address of first digit
;       EXIT    'C' clear if OK
;                 (AX) = value
;                 (SI) updated to first non-digit
;               'C' set if error - no digits, or result >65535
;                 (DS:SI) points to error character
;       USES    AX, SI, FLAGS

Procedure atod,,local
    assume ds:nothing,es:nothing,ss:DGROUP
        push    dx
        push    cx              ; save registers
        mov     al,[si]
        sub     al,'0'
        jc      short atod9     ; error - no digits
        cmp     al,10
        cmc
        jc      short atod9     ; error - no digits
        sub     ax,ax           ; clear accumulator
        mov     cx,10           ; base 10

;       crack next digit
;
;       (AX) = number accumulated so near
;       (CX) = 10
;       (DS:SI) = next character

atod1:  xchg    dx,ax           ; keep accum in dx for a while
        lodsb                   ; (al) = character
        sub     al,'0'
        jc      short atod7     ; not digit - all done
        cmp     al,9
        ja      short atod7     ; not digit - all done
        sub     ah,ah           ; (ax) = digit value (0 - 9)
        push    ax
        xchg    ax,dx
        mul     cx              ; (ax) = 10*accum
        pop     dx              ; (dx) = digit to add
        jo      short atod8     ; overflow
        add     ax,dx
        jmp     short atod1     ; go back for more

;       Done with number, all OK
;
;       (dx) = number
;       (ds:si) = address+1 of first unused character

atod7:  clc

;       Done with number, error
;       'C' set

atod8:  dec     si              ; backup over non-decimal (or error) char
atod9:  pop     cx
        xchg    ax,dx           ; (ax) = number iff no error
        pop     dx              ; restore registers
        ret                     ; exit
EndProc atod




;***    _dputchar - print ascii character on COM port
;
;       May be called in real mode or protected mode if IOPL is sufficient.
;
;       ENTRY   ((SS:SP+2)) = ascii character to print
;               (DS)        = DGROUP
;       EXIT    NONE
;       USES    AX, BX, Flags

Procedure _dputchar
    assume ds:DGROUP,es:nothing,ss:DGROUP
        mov     bx,sp
        mov     al,[bx+2]
        call    putchar
        ret
EndProc _dputchar

endif

;***LP  IndicateAltF1
;
;       This routine displays a message indicating ALT-F1 (or another
;       key combination) can now be pressed.
;
;       CALLABLE FROM REAL MODE ONLY - near ROUTINE
;
;       ENTRY   NONE
;       EXIT    NONE
;       USES    AX, Flags

Procedure IndicateAltF1,,local
        pusha
        push    es
        mov     ax,cs                ;
        mov     es,ax                ; Have es:bp point to string of blanks
        mov     bp,offset WRTSTRING
        mov     cx,09h               ; String has a count of 9 characters
        xor     dx,dx                ; Write string at cursur position 0,0
        mov     bx,dx                ; Page Number 0
        mov     ax,01302h            ; Function 13, sub-function 2
        int     10h                  ; write the string to the screen
        pop     es
        popa
        ret
EndProc IndicateAltF1


.8086                   ; Here to end of file is 8086 only code
                        ; except for OS2MEM stuff at end

COM1_PORT       equ     03f8H
COM2_PORT       equ     02f8H

COM_DAT         equ     00H
COM_IEN         equ     01H             ; interrupt enable
COM_IER         equ     02H             ; interrupt ID
COM_LCR         equ     03H             ; line control registers
COM_MCR         equ     04H             ; modem control register
COM_LSR         equ     05H             ; line status register
COM_MSR         equ     06H             ; modem status regiser
COM_DLL         equ     00H             ; divisor latch least sig
COM_DLM         equ     01H             ; divisor latch most sig

ifdef PMDEBUG
COMmsgs dw      offset COM1msg, offset COMNmsg
COM1msg db      "COM1", 0dh, 0ah, 0
COMNmsg db      "COMn", 0dh, 0ah, 0
endif


;***    initcom - initialize the com ports
;
;       initializes COM1 thru COM4 to 9600 baud
;
;       ENTRY   NONE
;       EXIT    NONE
;       USES    Flags

Procedure initcom
    assume ds:nothing,es:nothing,ss:nothing
        push    es
        push    ds
        push    di
        push    si
        push    dx
        push    ax

        cmp     [ComPortAddr],0
        jne     short ic90      ; don't do anything if already found port

        mov     ax,40h
        mov     es,ax           ; (ES) = ROM data area
    assume es:nothing

;       Check if there is a COM2 port

        xor     si,si
        xor     di,di
        mov     ax,COM2_PORT    ; (AX) = COM2 port address
        mov     cx,4
        cld
        repne scasw
        je      short ic00      ; jump if there is a COM2 port

;       Check if there is a COM1 port

        inc     si
        xor     di,di
        mov     ax,COM1_PORT    ; (AX) = COM1 port address
        mov     cx,4
        repne scasw
        je      short ic00      ; jump if there is a COM1 port

;       If there is no COM2 or COM1 port use the first port
;       address in the ROM data area.  If there are no com
;       ports, a zero will be moved into ax and put into
;       the com port addr variable.

        inc     si
        mov     ax,es:[0]       ; (AX) = other COM port address
ic00:   mov     [ComPortAddr],ax

;       program the usart(s)

        xor     di,di
        mov     cx,4
ic20:   mov     dx,es:[di]      ; get one port address
        inc     di
        inc     di
        or      dx,dx           ; is it valid?
        jz      short ic40      ; jmp if invalid

        add     dx,COM_LCR

        mov     al,80h
        out     dx,al           ; command it
        sub     al,al
        add     dx,COM_DLM-COM_LCR
        out     dx,al
        add     dx,COM_DLL-COM_DLM
        mov     al,12           ; 9600 baud = 12, 19.2 Kbaud = 6
        out     dx,al
        mov     al,3
        add     dx,COM_LCR-COM_DLL
        out     dx,al           ; command normal mode
ic40:   loop    ic20

ifdef PMDEBUG
        or      si,si
        jz      short ic90      ; jmp if we used COM2 (no message)
        dec     si
        mov     si,cs:[COMmsgs+si] ; else (si) = message offset
ic80:   lods    byte ptr cs:[si]
        or      al,al
        jz      short ic90
        call    putchar         ; print 1 msg char
        jmp     short ic80
endif

ic90:   pop     ax
        pop     dx
        pop     si
        pop     di
        pop     ds
        pop     es
        ret
EndProc initcom


ifdef PMDEBUG

;***    inchar - input character
;
;       USES    AX, Flags
;       EXIT    'Z' set if no character
;               'Z' clear if char
;                       (AL) = char

Procedure inchar
    assume ds:nothing,es:nothing,ss:nothing
        push    dx
        mov     dx,[ComPortAddr]
        add     dx,COM_LSR
        in      al,dx
        and     al,1
        jz      short ichx
        add     dx,COM_DAT-COM_LSR
        in      al,dx
        and     al,7fh
        jz      short ichx
        cmp     al,'C'-40h              ; is it a CTL-C?
        jnz     short ich40
        inc     [sawctlc]               ; if so, log it
ich40:
ich90:  or      al,al                   ; clear Zero Flag
        jz      short ichx
        mov     [_BreakChar],al         ; save possible kernel break char
ichx:   pop     dx
        ret
EndProc inchar


;***    putchar - put a character on the console
;
;       ENTRY   (AL) = character
;       EXIT    NONE
;       USES    NONE

Procedure putchar
    assume ds:nothing,es:nothing,ss:nothing
        pushf
        cli
        push    dx
        push    ax              ; (AL) = character

;       see if CTL-Q or CTL-S

        call    inchar
        jz      short ptc30     ; no characters incoming
        cmp     al,'S'-40h      ; CTL-S?
        jnz     short ptc30     ; no, ignore

;       Have CTL-S; wait till we see CTL-Q.

ptc20:  call    inchar
        jz      short ptc20
        cmp     al,'Q'-40h
        jnz     short ptc20

ptc30:  mov     dx,[ComPortAddr]
        add     dx,COM_LSR
ptc40:  in      al,dx
        test    al,20h
        jz      short ptc40

;       ready.  crank it out!

        add     dx,COM_DAT-COM_LSR
        pop     ax
        out     dx,al

        pop     dx
        popf
        ret
EndProc putchar

endif   ; PMDEBUG




;***LP  CpuTest - enforce that cpu type is a 386
;
;       This routine relies on Intel-approved code that takes advantage
;       of the documented behavior of the high nibble of the flag word
;       in the REAL MODE of the various processors.  The MSB (bit 15)
;       is always a one on the 8086 and 8088 and a zero on the 286 and
;       386.  Bit 14 (NT flag) and bits 13/12 (IOPL bit field) are
;       always zero on the 286, but can be set on the 386.
;
;       For future compatibility of this test, it is strongly recommended
;       that this specific instruction sequence be used.
;
;       CALLABLE FROM REAL MODE ONLY - near ROUTINE
;
;       ENTRY   NONE
;       EXIT    NONE
;       USES    AX, Flags

Procedure CpuTest,,local
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing
;       RMONLY
        pushf                           ; save entry flags
        xor     ax,ax                   ; (AX) = 0
        push    ax
        popf                            ; try to put 0 in the flags
        pushf
        pop     ax                      ; look at what really went into flags
        test    ax,8000h                ; Q: was high bit set ?
        jnz     short _BootErrCPU       ;   Y: 8086/8088
        mov     ax,F_NT or F_IOPLMASK   ;   N: try to set the NT/IOPL bits
        push    ax
        popf                            ;      ... in the flags
        sti                             ; (for VDMM/IOPL0)
        pushf
        pop     ax                      ; look at actual flags
        test    ax,F_NT or F_IOPLMASK   ; Q: any high bits set ?
        jz      short _BootErrCPU       ;   N: 80286
        popf                            ; restore entry flags
        ret
EndProc CpuTest

;***    _fatal         - bad kernel load image
;***    _BootErrCPU    - booting on 8086 or 80286
;***    _BootErrBPB    - boot BPB invalid
;***    _BootErrFSD    - bad FSD
;***    _BootErrKernel - kernel not found
;***    _BootErrMem    - not enough memory
;***    _BootErrRead   - disk read failure
;***    _BootErrOS2MEM - bad fake memory values in OS2MEM
;***    _BootErrECMOS  - bad eisa cmos
;       We've encountered one of the above non-recoverable errors;
;       print an appropriate message and hang the cpu, but allow reboot.

Procedure _fatal
    assume ds:nothing,es:nothing,ss:DGROUP
        mov     ax,MSG_SYSINIT_DOS_NOT_VAL
        jmp     short ftl00

Entry _BootErrMsgFile
        mov     ax,MSG_SYSINIT_LDR_MSG_NOT_FD
        jmp     short ftl00

Entry _BootErrBadMsgFile
        mov     ax,MSG_SYSINIT_LDR_MSG_NOT_VAL
        jmp     short ftl00

Entry _BootErrCPU,,local
        mov     ax,MSG_NOT_80386_CPU
        jmp     short ftl00

Entry _BootErrBPB
Entry _BootErrFSD
        mov     ax,MSG_SYSINIT_FSD_NOT_VAL
        jmp     short ftl00

Entry _BootErrKernel
        mov     ax,MSG_SYSINIT_DOS_NOT_FD
        jmp     short ftl00

ifdef OS2MEM
Entry _BootErrOS2MEM
endif
Entry _BootErrMem
        mov     ax,MSG_SYSINIT_MORE_MEM
        jmp     short ftl00

Entry _BootErrECMOS                            ; 119727 begins
        mov     ax,MSG_SYSINIT_CMOS_ERR
        jmp     short ftl00                    ; 119727 ends

Entry _BootErrRead
        mov     ax,MSG_SYSINIT_BOOT_ERROR
ftl00:

ifdef PMDEBUG
        push    ds
        push    si
endif

        push    ax
        push    cs                              ; Make CrLfMsg addressable
        pop     ds
        mov     si,offset CrLfMsg
        call    ErrPrt
        pop     ax
        call    RealGetMessage                  ; message address in DS:SI
        inc     si                              ; skip message length
        inc     si
        call    ErrPrt
        mov     ax,MSG_SYSINIT_INSER_DK
        call    RealGetMessage                  ; message address in DS:SI
        inc     si                              ; skip message length
        inc     si
        call    ErrPrt
ifdef PMDEBUG
    ifdef CALLDEBUGGER
        call    Debug_Entry
    endif
endif

ifdef PMDEBUG
        pop    si
        pop    ds
        ret
endif
        sti
        jmp     short $                 ; loop forever

EndProc _fatal

;***    ErrPrt - print an asciiz string on the console
;
;       ENTRY   DS:SI -> asciiz string
;       EXIT    NONE
;       USES    AX, BX, SI, Flags

Procedure ErrPrt,,local
    assume ds:nothing,es:nothing,ss:DGROUP
        push    ax
        push    bx
        cld                             ; make sure direcion is up
erp00:  lodsb                           ; get next character
        or      al,al
        jz      short erpx
ifdef PMDEBUG
        call    putchar                 ; write to debugging port
endif
        mov     ah,14                   ; write teletype
        mov     bx,7                    ; attribute
        int     10h                     ; print it
        jmp     short erp00

erpx:   pop     bx
        pop     ax
        ret
EndProc ErrPrt

ifdef OS2MEM
.386
;***   _BootMsgOS2MEM - Display error message for OS2MEM
;
;       ENTRY   NONE
;       EXIT    NONE
;       USES    AX, Flags
Procedure _BootMsgOS2MEM
    assume ds:nothing,es:nothing,ss:DGROUP
        push    bx
        push    si
        push    ds
        push    ecx

        push    cs
        pop     ds
        mov     si,offset OS2MEMErrMsg
        call    ErrPrt

        mov     ecx,00f00000h   ; Delay so user will see message
bmOS2MEM:
        enter   0,0             ; Expensive instruction
        leave                   ; Expensive instruction
        dec     ecx             ;
        jnz     bmOS2MEM        ;


        pop     ecx
        pop     ds
        pop     si
        pop     bx
        ret
EndProc _BootMsgOS2MEM
endif


_TEXT ends

ifndef HYBERI13
.386p
_TEXT32 SEGMENT
    assume cs:DGROUP

;       esi = phys address of hyberldr
;       edi = phys address of hyberInfoHdr

Procedure main32,far

        ; set stack
        mov     esp, edi
        add     esp, PAGESIZE-16
        mov     ebp, esp

        mov     eax, edi                        ; hyberInfoHdr is param
        mov     ebx, esi
        jmp     ebx                             ; to hyberldr

EndProc main32

_TEXT32 ends
endif ;HYBERI13

        end
