page    60,132
; ****************************************************************************
; *                                                                          *
; *                       IBM Confidential                                   *
; *                                                                          *
; *                 Copyright (c) IBM Corporation  1996                      *
; *                           All Rights Reserved                            *
; *                                                                          *
; ****************************************************************************
;
; unhybernate.asm - restore system memory and registers from disk.
;
; Entry:    called by os2ldr with es:di pointing to hyberInfoHdr
;
; Exit:     By jump to the restart entry pointed to by the hyberInfoHdr
;
; Change History
;
;  5/03/96  153804  cgw  initial version
;  5/09/96  154412  cgw  change messages to use less space
;  5/09/96  154438  cgw  use drive number from infohdr
;  5/25/96  155605  cgw  clean up debug msgs to reduce memory use
;  5/31/96  153620  tpl  do recovery of IVT, BIOS Data Area last
;  8/16/96  166339  MT   do recovery of extended bios data area
;  8/26/96  167172  MT   Make sure copied code & stack lies within 1 page
;                        Correct a sector count
;*****************************************************************************
        name    unhyber

        .xlist
        include segments.inc    ; segment declarations
        include basemaca.inc
        include dbprtdef.inc    ; DBPRT macro definition
        include hybernat.inc
        include hyberhd.inc
        include int.inc
        include doshlp.inc
        .list

if HYBERDEBUG_R GE 2
        extrn   _dispRegs:near
        extrn   _dispStg:near
endif ;HYBERDEBUG_R GE 2
        extrn   DHEnableA20:near

PAGESIZE        equ     4096
PAGEMASK        equ     PAGESIZE - 1
PAGESHIFT       equ     12
PAGEDIRSHIFT    equ     22      ; to convert from lin addr to pde
TEMPGDT         equ     60h     ; 2 gdt entries used to map real mode segments

RTCCTL          equ     070h
RTCDTA          equ     071h

MI_OPERANDSIZE  equ     66h

DELAY   macro
        local   dly

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

_DATA segment
relocptr        dd      ?
_DATA ends


_TEXT segment
    assume cs:DGROUP
.386p

;*********************************************
; macro  : outchar
;          Low level routine that performs in's
;          and out's to send a character out
;          the specified com port
; Entry  : AL
; Exit   : None
; Uses   : None
;*********************************************

COM1      EQU     03f8h      ; COM1
COM2      EQU     02f8h      ; COM2
COM_PORT  EQU     COM1       ; Default COM Port

outchar   macro   char
          local   oc1
          pushf
          push    ax
          push    dx
          mov     dx,COM_PORT
          push    dx
          add     dx,5
oc1:      in      al,dx
          test    al,020H
          jz      short oc1
          pop     dx
          mov     al,char
          out     dx,al
          pop     dx
          pop     ax
          popf
          endm

;-----------------------------------------------------------------------------
; unhyber - main entry from os2ldr - ES:DI -> hyberinfohdr
;-----------------------------------------------------------------------------
        public  unhyber
Procedure unhyber
        assume  ds:DGROUP

;-----------------------------------------------------------------------------
; Print some debug info using loader's print service
;-----------------------------------------------------------------------------
if HYBERDEBUG_R GE 2
        DBPRT   17,1,<in unhyber \n>
        call    _dispRegs

        push    ds
        assume  ds:NOTHING
        DBPRT   17,1,<hyberInfoHdr (es:di) -\n>
        push    es
        pop     ds
        mov     si,di
        mov     cx,0200h                                                ;153620
        call    _dispStg
        pop     ds
        assume  ds:DGROUP
endif ;HYBERDEBUG_R GE 2

;-----------------------------------------------------------------------------
; Copy hyberInfoHdr to hyberdata reserved area
;-----------------------------------------------------------------------------
        push    es
        pop     gs
        mov     si,di                  ; source = gs:di
;       xor     eax,eax
        movzx   eax,word ptr es:hyberInfoHdr_s.hih_dataloc[di]
        shl     eax,PAGESHIFT-4        ; convert page to seg addr
        mov     es,ax
        xor     di,di                  ; dest = es:di
        mov     ecx,PAGESIZE/4
if HYBERDEBUG_R GE 3
        call    _dispRegs
endif ;HYBERDEBUG_R GE 3
    rep movs    es:dword ptr [di], gs:dword ptr [si]     ; copy data
        shl     eax,4                  ; convert seg to flat addr
        mov     edi,eax                ; keep hyberinfohdr around in edi

;-----------------------------------------------------------------------------
; Enable A20 for later when ES munged
;-----------------------------------------------------------------------------
        cli
        call    DHEnableA20
        cli

;-----------------------------------------------------------------------------
; Move remainder of this code to the hyberloader reserved area
;-----------------------------------------------------------------------------
        call    _MungeES
        push    ds
        push    edi

        push    cs
        pop     ds
        assume  ds:NOTHING
        mov     si,offset DGROUP:copystart    ; ds:si = source address

        mov     ax,es:hyberInfoHdr_s.hih_hldrloc[edi]
        shl     ax,PAGESHIFT-4
        mov     bx,offset DGROUP:copystart
        shr     bx,4
        sub     ax,bx
        mov     es,ax                  ; es:di = dest addr, adjusted so that
        mov     di,si                  ; offset (si) is the same as src (di)
        mov     ecx,offset DGROUP:copyend + 3 ; this could be 1 stmt, but masm
        sub     ecx,offset DGROUP:copystart   ; says phase error (masm bug ?)
        shr     ecx,2
        cld

if HYBERDEBUG_R GE 2
        DBPRT   17,1,<At copy code, copy size = $x dwords\n>,<cx>
        call    _dispRegs
endif ;HYBERDEBUG_R GE 2
    rep movs     es:dword ptr [di],ds:dword ptr [si]     ; copy data

        pop     edi

        mov     word ptr relocptr+2,es ; set reloc ptr to new @ of unh_cntl
        mov     word ptr relocptr,offset DGROUP:unh_cntl

if HYBERDEBUG_R GE 3
        lds     si,relocptr
        mov     cx,020h
        call    _dispStg
endif ;HYBERDEBUG_R GE 3
        pop     ds
        assume  ds:DGROUP

;-----------------------------------------------------------------------------
; Move stack to end of the hyberloader reserved area
;-----------------------------------------------------------------------------
if HYBERDEBUG_R GE 3
        DBPRT   17,1,<new ss:sp in es:ax\n>
        mov     ax,offset DGROUP:copystart + PAGESIZE - 16-4    ;167172
        and     ax,0FFFEh                                       ;167172
        call    _dispRegs
endif ;HYBERDEBUG_R GE 3

        push    es
        pop     ss
        mov     sp,offset DGROUP:copystart + PAGESIZE - 16-4    ;167172
        and     sp,0FFFEh                                       ;167172

;-----------------------------------------------------------------------------
; call copy of unh_cntl relocated to hyberloader area, never returns
;-----------------------------------------------------------------------------
if HYBERDEBUG_R GE 1
        DBPRT   17,1,<calling unh_cntl\n>
endif ;HYBERDEBUG_R GE 1

        call    dword ptr relocptr

EndProc unhyber

;-----------------------------------------------------------------------------
; This code will be copied to a new location in memory.  From copystart
; to copyend will be copied to the hyberloader area before being executed.
; This is so it is not overlaid by storage data as it is read back in from
; disk.  Only CS relative relocation are allowed in this area.  This
; code will also never return, it exits via restarting the hybernated system.
;-----------------------------------------------------------------------------
copystart:
;##############################################################################
;# DBGMSG - real mode debug message macros and subroutines
;#
;# If DBGMSG = 1 then this will expand into code that uses the BIOS to
;# output messages to either the screen or the serial port.  The code and
;# macros depend only on the stack and code segments being valid.  All
;# registers that are touched are saved and restored.
;#
;# macros:
;#     dbgmsg   0,"text\n"              displays text; \n translates to crlf
;#     dbgmsg   n,value                 displays value as an n byte hex number
;#
;# public symbol dbgmsgflag enables message output:
;#     01 = output on serial
;#     02 = output on video
;# default is 01.
;#
;##############################################################################
if HYBERDEBUG_R GE 1
        public   dbgmsgflag
dbgmsgflag:
        db      01h
dbgmsgcomport:
        db      01h                    ; bios com port 01 = com2

dbgmsg macro  text
endm

dbgmsg  macro  len,value,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16
        local  msg, pastmsg
     IFNB   <len>
     IF len EQ 0
        jmp    short pastmsg
msg:    db     value
        db     0
pastmsg:
        push   offset msg
     ELSEIF len EQ 1
        push   value
     ELSEIF len EQ 2
        push   value
     ELSEIF len EQ 4
        push   value
     ELSE
        .ERR <Invalid len argument, must be 1, 2, or 4>
     ENDIF
        push   len
        call   dbgmsgsub
        dbgmsg  p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16
     ENDIF
endm

dbgmsgxbuf:
        db      "abcdefgh",0

dbgmsgsub:
        assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing
        pushf
        pushad


        mov    bp,sp
        mov    cx,ss:[bp+36]           ; move length to ebx
        mov    ebx,ss:[bp+38]          ; move value to ebx

        jcxz   short dbgmsgout         ; 0 means text, go write it
        shl    cx,1                    ; loop twice for each byte
        mov    si, offset dbgmsgxbuf+7; point to end of cvt buf
dbgmsgsub_xloop:
        mov    al,bl                   ; put nibble in al[3:0]
        and    al,00Fh                 ; mask off extra 4 bits
        add    al,030h                 ; convert to ascii
        cmp    al,039h                 ; compare with "9"
        jle    short dbgmsgsub_isdec   ; "9" or less is ok
        add    al,007h                 ; fixup to "A" - "F"
dbgmsgsub_isdec:
        mov    cs:[si],al
        dec    si
        shr    ebx,4                   ; get next 4 bits in bl[3:0]
        loop   dbgmsgsub_xloop         ; repeat for all nibbles
        mov    bx, si                  ; point to first char in buf
        inc    bx

dbgmsgout:
        mov    cx,1                    ; cx = rep count for write attr
        mov    si,bx
        cld

dbgmsg_loop:
        lods   byte ptr cs:[si]     ; pick up character
        cmp    al,0                 ; test for end of string
        je     short dbgmsgdone
        cmp    al,"\"               ; is it a backslash
        jne    short dbgmsg_outchar
        lods   byte ptr cs:[si]     ; pick up escape code
        cmp    al,0                 ; test for end of string
        je     short dbgmsgdone
        cmp    al,"n"               ; only newline is recognized
        jne    short dbgmsg_outchar
        mov    al,0Dh               ; replace with CR
dbgmsg_outchar:
        test   byte ptr dbgmsgflag,01h
        jz     short dbgmsgnoser
        mov    ah,01h               ; ah=01 = write character to port
;       xor    dx,dx
        movzx  dx,byte ptr dbgmsgcomport; dx=port; 0=com1, 1=com2
        int    14h

dbgmsgnoser:
        test   byte ptr dbgmsgflag,02h
        jz     short dbgmsgnovid
        mov    bx,00002h            ; 02 = black bg, green fg
        cmp    al,020h              ; test for ctl char
        jl     short dbgmsgwrite
        mov    ah,009h              ; ah=09 = write attr
        int    010h
dbgmsgwrite:
        mov    ah,00eh              ; ah=0e = write tty
        int    010h
dbgmsgnovid:
        cmp    al,0Dh
        jne    short dbgmsg_loop
        mov    al,0Ah               ; add LF to CR
        jmp    short dbgmsg_outchar

dbgmsgdone:

        mov    ax,es                   ; assume if es = 0008
        cmp    ax,0008h                ;   that it was munged and
        jne    short dbgmsg_es_ok      ;   must be remunged.
        call   _MungeES
dbgmsg_es_ok:
        mov    cx,ss:[bp+36]           ; move length to ebx
        cmp    cx,4
        jge    short dbgmsg_dwret
        popad
        popf
        ret    4                       ; pop word len + word parm
dbgmsg_dwret:
        popad
        popf
        ret    6                       ; pop word len + dword parm
else ;HYBERDEBUG_R GE 1

dbgmsg macro  text
endm

dbgmsgx macro  len,src
endm

endif ;HYBERDEBUG_R GE 1

;-----------------------------------------------------------------------------
; unh_cntl - call unh_load, then unh_rst
;-----------------------------------------------------------------------------
Procedure unh_cntl,far
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing
        EnterProc                      ; on entry: EDI -> hyberinfohdr
        call    _MungeES               ; 4G flat - good until ES reloaded
if HYBERDEBUG_R GE 1                                                    ;153620
        sub     bx,bx                  ; BIOS COM port location         ;153620
        mov     ax,40h                 ; Check 40:0 for COM port        ;153620
        mov     ds,ax                  ;                                ;153620
        mov     ax,es:hih_debugport[edi]                                ;153620
        mov     cx,4                   ; check 4 COM port locations     ;153620
checkCOM:                                                               ;153620
        cmp     ax,[bx]                ; Found COM port in 40:xx ?      ;153620
        je      short foundCOM         ; no, leave it at com2           ;153620
        add     bx,2                                                    ;153620
        dec     cx                                                      ;153620
        jz      short noCOM                                             ;153620
foundCOM:                                                               ;153620
        shr     bx,1                                                    ;153620
        mov     byte ptr dbgmsgcomport,bl                               ;153620
        dbgmsg  0,"in unh_cntl, comport=",2,ax,0,"\n"
noCOM:                                                                  ;153620
endif ;HYBERDEBUG_R GE 1

if HYBERDEBUG_R GE 2
        dbgmsg   0,"call unh_load\n"
endif ;HYBERDEBUG_R GE 2
        call     near ptr unh_load
        or       ax,ax
        jnz      short reboot

if HYBERDEBUG_R GE 2
        dbgmsg   0,"call unh_rst\n"
endif ;HYBERDEBUG_R GE 2
        call     near ptr unh_rst        ; only returns if error
if HYBERDEBUG_R GE 2
        dbgmsg   0,"ret from unh_rst, rebooting\n"
endif ;HYBERDEBUG_R GE 2

reboot:
        db      0EAh,00h,00h,0FFh,0FFh

        LeaveProc
EndProc unh_cntl


;-----------------------------------------------------------------------------
; unh_load - read saved memory image into physical memory
;-----------------------------------------------------------------------------
Procedure unh_load                     ; on entry: EDI -> hyberinfohdr
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing
    LocalVar    diskl, DWORD           ; current disk list entry addr
    LocalVar    disklm, DWORD          ; disk list entry max addr
    LocalVar    diskaa,DWORD           ; current disk area sector addr
    LocalVar    diskal,DWORD           ; current disk area sector len
    LocalVar    pagel, DWORD           ; current page list entry addr
    LocalVar    pagelm, DWORD          ; page list entry max addr
    LocalVar    pageaa,DWORD           ; current page area physical addr
    LocalVar    pageal,DWORD           ; current page area page len
    LocalVar    iobuf, DWORD           ; < 1M I/O buffer addr
    LocalVar    iobufsz, DWORD         ; I/O buffer len in bytes
    LocalVar    diskls, DWORD          ; 1st dlist saved
    LocalVar    ncyl,WORD              ; num of cylinders
    LocalVar    nsec,WORD              ; num sectors per track
    LocalVar    nhead,WORD             ; num of heads
    LocalVar    secpcyl,WORD           ; sectors per cylinder
    LocalVar    drive,WORD             ; drive - 0080 = physical drive 0

        EnterProc

        mov      cx,sp
        pushf
        pop      bx


if HYBERDEBUG_R GE 3
        dbgmsg   0,"in unh_load ss:sp=",2,ss,0,":",2,cx,0," edi="
        dbgmsg   4,edi,0," flag=",2,bx,0,"\n"
endif ;HYBERDEBUG_R GE 3
        push    edi                    ; save flat pointer to hyberinfohdr

        mov     eax,edi
        add     eax,es:dlist[edi]      ; eax = offset to hyberinfohdr->dlist
        mov     diskl,eax
        mov     diskls,eax             ; save it for IVT transfer       ;153620

        mov     ebx,es:hih_dli[edi]
        shl     ebx,3
        add     eax,ebx                ; eax = offset to last dlist, excluding
        mov     disklm,eax             ;       dlist to swapper2.dat

        mov     eax,edi
        add     eax,es:plist[edi]      ; eax = offset to hyberinfohdr->plist
        mov     pagel,eax

        mov     ebx,es:hih_pli[edi]
        shl     ebx,2
        add     eax,ebx                ; eax = offset to last plist, excluding
        mov     pagelm,eax             ;       plist to swapper2.dat

;       xor     eax,eax
        movzx   eax,word ptr es:hih_iobuflen[edi]
        shl     eax,PAGESHIFT          ; convert iobuflen from page(s) to bytes
        mov     iobufsz,eax

;       xor     eax,eax
        movzx   eax,word ptr es:hih_iobuf[edi]
        shl     eax,PAGESHIFT          ; convert iobuf from page frame to
        mov     iobuf,eax              ;   physical address

;       xor     ax,ax
        movzx   ax,byte ptr es:hih_drive[edi]
        mov     drive,ax               ; get drive ID (ie. 80h = 'C' drive)

        shr     iobufsz,9              ; change iobufsz from bytes to sectors
;-----------------------------------------------------------------------------
; dump lists for debug
;-----------------------------------------------------------------------------
if HYBERDEBUG_R GE 3
        mov      ebx,diskl
        dbgmsg   0,"diskl=",4,ebx
        mov      ecx,disklm                                             ;153620
        dbgmsg   0," disklm=",4,ecx,0,"\n"                              ;153620
nextdlist:
        mov      edx,es:[ebx]           ; display the entire dlist, including
        dbgmsg   0,"sect=",4,edx        ; the swapper2.dat dlist
        add      ebx,4
        mov      edx,es:[ebx]
        dbgmsg   0," sct=",4,edx,0,"\n"
        add      ebx,4
        cmp      ebx,pagel                                              ;153620
        jb       short nextdlist                                        ;153620


        mov      ebx,pagel
        dbgmsg   0,"pagel=",4,ebx
        mov      ecx,pagelm                                             ;153620
        dbgmsg   0," pagelm=",4,ecx,0,"\n"                              ;153620
nextplist:
        mov      dx,es:[ebx]            ; display the entire plist, including
        dbgmsg   0,"pg=",2,dx           ; the swapper2.dat plist
        add      ebx,2
        mov      dx,es:[ebx]
        or       dx,dx                                                  ;153620
        jz       short getdrv                                           ;153620
        dbgmsg   0," pct=",2,dx,0,"\n"
        add      ebx,2
        jmp      short nextplist                                        ;153620

        mov      ebx,iobuf
        dbgmsg   0,"iobuf=",4,ebx
        mov      ecx,iobufsz
        dbgmsg   0," iobufsz=",4,ecx,0,"\n"
endif ;HYBERDEBUG_R GE 3
getdrv:
;-----------------------------------------------------------------------------
; get drive parms, store in local vars nhead, nsec, ncyl; calculate secpcyl
;-----------------------------------------------------------------------------
        mov      ah,08h                ; ah = get drive parameters
        mov      dl,byte ptr drive     ; dl = drive number
        int      13h                   ; returns: dh = max head num
        jc       errgdp                ;          cl[5:0] = max sec num
        inc      dh                    ;          cl[7:6] || ch = max cyl
        shr      dx,8
        mov      nhead,dx
        mov      al,cl
        and      ax,003Fh
        mov      nsec,ax
        shr      cl,6
        ror      cx,8
        inc      cx
        mov      ncyl,cx
        mov      ax,nsec
        mul      nhead
        mov      secpcyl,ax

if HYBERDEBUG_R GE 2
        dbgmsg   0,"drive=",1,drive,0," nhead=",2,nhead,0," nsec=",2,nsec
        dbgmsg   0," ncyl=",2,ncyl,0," secpcyl=",2,secpcyl,0,"\n"
endif ;HYBERDEBUG_R GE 2

        call     _MungeES              ; 4G flat - good until ES reloaded

;-----------------------------------------------------------------------------
; Initialze disk and page area variables from first list entries
;-----------------------------------------------------------------------------
        mov      edx,diskl
        mov      eax,es:[edx]          ; eax = disk list[0] sector addr
        add      eax,PAGESIZE/512                                       ;153620
        mov      diskaa,eax            ; save in diskaa
        mov      eax,es:[edx+4]        ; eax = disk list[0] sector count
        sub      eax,PAGESIZE/512                                       ;153620
        js       errdle                ; IVT crosses dlist              ;153620
        mov      diskal,eax            ; save in diskal
        add      edx,8
        mov      diskl,edx             ; point to next disk entry

        mov      edx,pagel
;       xor      eax,eax
        movzx    eax,word ptr es:[edx] ; ax = page list[0] page num
        inc      eax                                                    ;153620

        shl      eax,12                ; convert to physical address
        mov      pageaa,eax            ; save in pageaa
;       xor      eax,eax
        movzx    eax,word ptr es:[edx+2] ; eax = page list[0] page count
        dec      eax                                                    ;153620
        shl      eax,3                 ; convert pages to sectors
        mov      pageal,eax            ; save in pageal
        add      edx,4
        mov      pagel,edx             ; point to next page entry

;-----------------------------------------------------------------------------
; Find the largest contiguous area in both the current disk and page areas
;-----------------------------------------------------------------------------
unh_loadloop:
        mov      ebx,diskal            ; ebx = remaining sectors in disk area
        mov      eax,pageal            ; eax = remaining sectors in page area
        cmp      eax,ebx               ;
        jg       short gotsize         ; jump if page area larger
        mov      ebx,eax               ; replace disk sz w/smaller page sz
gotsize:                               ; here, ebx=min(disk sz, page sz) in sec
;-----------------------------------------------------------------------------
; call unh_read to read it from disk
;-----------------------------------------------------------------------------
        push     ebx                   ; save sector count across call
        push     drive
        push     nsec
        push     secpcyl
        push     iobufsz
        push     iobuf
        push     ebx                   ; sector count
        push     pageaa                ; page address
        push     diskaa                ; sector number

        call     unh_read
        pop      ebx                   ; restore sector count
        or       ax,ax
        jnz      unh_loadexit

;-----------------------------------------------------------------------------
; update disk and page area addresses and lengths by amount written
;-----------------------------------------------------------------------------
        add      diskaa,ebx            ; increment disk area address
        sub      diskal,ebx            ; decrement disk area len
        jg       short moredisk        ; jump if area not exausted

        mov      diskal,0              ; set length to zero
        mov      edx,diskl             ; dx = next disk list entry addr
        cmp      edx,disklm            ; compare it with the max addr
        jg       short moredisk        ; jump if no more disk areas (diskal
                                       ;      is already zero, used below)

        mov      eax,es:[edx]          ; eax = next disk list sector address
        mov      diskaa,eax            ; save it in diskaa
        mov      eax,es:[edx+4]        ; eax = next disk list sector count
        mov      diskal,eax            ; save it in diskal
        add      edx,8
        mov      diskl,edx             ; point to next disk list entry
moredisk:

        mov      eax,ebx               ; eax = sector count
        shl      eax,9                 ; convert it to a byte count
        add      pageaa,eax            ; increment page area address
        sub      pageal,ebx            ; decrement page area len
        jg       short morepage        ; jump if area not exausted

        mov      pageal,0              ; set length to zero
        mov      edx,pagel             ; dx = next page list entry addr
        cmp      edx,pagelm            ; compare it with the max addr
        jg       short morepage        ; jump if no more page areas (pageal
                                       ;      is already zero, used below)
;       xor      eax,eax
        movzx    eax,word ptr es:[edx] ; eax = next page list page number
        shl      eax,12                ; convert to physical address
        mov      pageaa,eax            ; save it in pageaa
;       xor      eax,eax
        movzx    eax,word ptr es:[edx+2] ; eax = next page list page count
        shl      eax,3                 ; convert pages to sectors
        mov      pageal,eax            ; save it in pageal
        add      edx,4
        mov      pagel,edx             ; point to next page list entry
morepage:

;-----------------------------------------------------------------------------
; check for more data to read and disk list exausted conditions
;-----------------------------------------------------------------------------
        cmp      pageal,0
        je       short unh_loadexitok  ; no more pages; we're done
        cmp      diskal,0
        je       errdle                ; disk list exausted, we're toast
        jmp      unh_loadloop          ; more pages, more disk; keep going

;-----------------------------------------------------------------------------
; Exit
;-----------------------------------------------------------------------------
unh_loadexitok:

        dbgmsg   0,"unh_loadexitok\n"
        mov      edx,diskls
        mov      ebx,PAGESIZE/512
        push     drive
        push     nsec
        push     secpcyl
        push     iobufsz
        push     iobuf
        push     ebx                   ; sector count
        push     iobuf                 ; page address
        push     dword ptr es:[edx]    ; sector number

        call     unh_read
        or       ax,ax
        jnz      unh_loadexit

if HYBERDEBUG_R GE 3
        mov      edi,iobuf
        cld
        mov      cx,50h
nextIVT:
        mov      eax,dword ptr es:[edi]
        add      edi,4
        dbgmsg   4,eax,0,"\n"
        loop     short nextIVT
endif ;HYBERDEBUG_R GE 3

        ;cli
        mov      esi,iobuf
        sub      edi,edi
        mov      cx,PAGESIZE/4
        cld
    rep movs     es:dword ptr [edi],es:dword ptr [esi]  ; copy data

        cmp      dword ptr cs:[XBDA_Sector], 0
        je       unh_NoXBDA
        mov      word ptr cs:[XBDA_IO], 1
        dbgmsg   0,"read in xbda sector!!!\n"
        push     drive
        push     nsec
        push     secpcyl
        push     iobufsz
        push     iobuf
        push     dword ptr cs:[XBDA_SectorCount]
        push     iobuf
        push     dword ptr cs:[XBDA_Sector]

        call     unh_read
        or       ax,ax
        jnz      short unh_loadexit

        cli
        mov      esi,iobuf
        movzx    edi,word ptr es:[40eh]
        shl      edi,4
        mov      ecx,dword ptr cs:[XBDA_SectorCount]
        shl      ecx,7
        cld
    rep movs     es:dword ptr [edi],es:dword ptr [esi]  ; copy data

unh_NoXBDA:
        xor      ax,ax                 ; set no error rc

unh_loadexit:
        pop      edi

        dbgmsg   0,"unh_load done, ax=",2,ax,0,"\n"

        LeaveProc
        ret

;-----------------------------------------------------------------------------
; error return code setups
;-----------------------------------------------------------------------------
errgdp:
        mov      ah,01h
        jmp      short unh_loadexit
errdle:
        mov      ax,0201h
        jmp      short unh_loadexit

EndProc unh_load

align 4
XBDA_Seg         dw     0
XBDA_IO          dw     0
XBDA_Sector      dd     0
XBDA_SectorCount dd     0

Procedure unh_read,near
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing
    ArgVar      sect,      DWORD       ; absolute sector on disk
    ArgVar      dest,      DWORD       ; physical data address
    ArgVar      secct,     DWORD       ; count of sectors to read
    ArgVar      iobuf,     DWORD       ; physical buffer addr (< 1M)
    ArgVar      iobufsz,   DWORD       ; size of buf (sectors)
    ArgVar      secpcyl,   WORD        ; sectors per cylinder
    ArgVar      secptrk,   WORD        ; sectors per track
    ArgVar      drive,     WORD        ; drive ID (80 = drive 0)
        EnterProc

if HYBERDEBUG_R GE 2
        dbgmsg   0,"unh_read starting sect=",4,sect,0," dest=",4,dest,0," secct="
        dbgmsg   4,secct,0," iobuf=",4,iobuf,0,"\n     iobufsz=",4,iobufsz
        dbgmsg   0," secpcyl=",2,secpcyl,0," secptrk=",2,secptrk,0," drive=",2,drive,0,"\n"
endif ;HYBERDEBUG_R GE 2

        cmp      dest,100000h           ;
        jge      unh_readloop           ; Normal if dest > 1M
        cmp      word ptr cs:[XBDA_IO], 1
        je       unh_readloop
        xor      ebx,ebx
        push     es
        mov      es,bx
        mov      bx, word ptr es:[40eh] ; get XBDA
        pop      es
        cmp      bx, 0
        je       unh_readloop           ; Normal if no XBDA presents
        shl      ebx,4                  ; convert to physical addr
        cmp      dest,ebx
        jg       unh_readloop           ; Normal if dest > XBDA addr
        mov      eax,secct              ; Find out the ending addr of this operation
        shl      eax,9
        add      eax,dest
        cmp      eax,ebx
        jbe      unh_readloop           ; Normal if ending addr < XBDA addr
        sub      eax,ebx                ; XBDA, need to change sector count
        shr      eax,9
        mov      dword ptr cs:[XBDA_SectorCount], eax
        sub      secct,eax              ; now set new sector count
        mov      eax,secct              ; 167172
        add      eax,sect               ;
        mov      dword ptr cs:[XBDA_Sector], eax
        dbgmsg   0,"unh_read new secct=",4,secct,0,"\n"

;-----------------------------------------------------------------------------
; calculate read length
;-----------------------------------------------------------------------------
unh_readloop:
        mov      ebx,secct             ; ebx = sector count
        mov      eax,100000h           ;
        sub      eax,dest              ; eax = bytes of data < 1M
        jle      short bufcomp         ; jump if data addr >= 1M
        shr      eax,9                 ; eax = sectors of data < 1M
        cmp      eax,ebx               ;
        jge      short bufcomp         ; jump if sect < 1M GE sector ct
        mov      ebx,eax               ; ebx = sect ct < 1M (which is smaller)

bufcomp:                               ; here, ebx = min(secct, sectors < 1M)
        mov      eax,iobufsz
        cmp      eax,ebx
        jge      short gotcount        ; jump if buf size GE sect ct
        mov      ebx,eax               ; ebx = buf sz (which is smaller)

gotcount:                              ; here, ebx = count for copy / read
;-----------------------------------------------------------------------------
; copy data if above 1M
;-----------------------------------------------------------------------------
        mov      eax,iobuf             ; eax = iobuf physical address
        mov      esi,dest              ; si = dest data address
        cmp      esi,100000h           ; compare with 1 Meg
        jge      short useiobuf        ; jump if below 1M
        mov      eax,dest              ; eax = dest data address

useiobuf:
        shr      eax,4                 ; ax = read paragraph address
        mov      es,ax                 ; es = destination segment
                                       ; here, ebx = sect ct and es =
                                       ;   dest segment
;-----------------------------------------------------------------------------
; read data from disk
;-----------------------------------------------------------------------------
        xor      edx,edx
        mov      eax,sect              ; edx:eax = disk sector
;       xor      ecx,ecx
        movzx    ecx,word ptr secpcyl  ; ecx = sectors per cyl
        div      ecx                   ; eax = cyl, edx = sect within cyl
        cmp      eax,3FFh
        jg       errc2b                ; jump if max cyl size exceeded
        mov      di,ax                 ; di = cylinder address
        mov      eax,edx
        xor      edx,edx               ; edx:eax = sect within cyl
        mov      cx,secptrk            ; ecx = sectors per track
        div      ecx                   ; eax = head, edx = sect within track


        mov      cx,di                 ; cx[9:0] = cyl
        rol      cx,8                  ; cx[1:0] || cx[15:8] = cyl
        shl      cl,6                  ; cx[7:6] || cx[15:8] = cyl
        inc      dl                    ; since sector is 1 based
        or       cl,dl                 ; cx = cccccccc ccssssss
        mov      dh,al                 ; dh = head
        mov      dl,byte ptr drive     ; dl = drive (C: usually = 80h)
        mov      ah,02h                ; ah = read sectors command
        mov      al,bl                 ; al = sector count
        push     ebx
        xor      bx,bx                 ; es:bx = data address

if HYBERDEBUG_R GE 3
        dbgmsg   0,"unh_read int 13 ax=",2,ax,0," cx=",2,cx,0," dx="
        dbgmsg   2,dx,0," es:bx=",2,es,0,":",2,bx,0,"\n"
endif ;HYBERDEBUG_R GE 3

        int      13h                   ; do it

        pop      ebx
if HYBERDEBUG_R GE 3                                                    ;153620
        jc       erri13w                                                ;153620
else                                                                    ;153620
        jc       erri13w
endif ;HYBERDEBUG_R GE 3                                                ;153620

;-----------------------------------------------------------------------------
; copy data if above 1M
;-----------------------------------------------------------------------------
        push     ebx
        call     _MungeES              ; 4G flat - good until ES reloaded
        pop      ebx

        mov      edi,dest              ; si = dest data address
        cmp      edi,100000h           ; compare with 1 Meg
if HYBERDEBUG_R GE 3                                                    ;153620
        jl       nocopy                ; jump if below 1M               ;153620
else                                                                    ;153620
        jl       nocopy          ; jump if below 1M
endif ;HYBERDEBUG_R GE 3                                                ;153620
        mov      eax,iobuf             ; eax = iobuf physical address
        shr      eax,4                 ; ax = iobuf paragraph address
        mov      gs,ax                 ; es = destination segment
        xor      esi,esi               ; clear source index
        mov      ecx,ebx               ; ecx = copy count in sectors
        shl      ecx,7                 ; ecx = copy count in dwords

if HYBERDEBUG_R GE 3
        dbgmsg   0,"unh_read copy from=",2,gs,0,":",4,esi,0," to="
        dbgmsg   2,es,0,":",4,edi,0," count=",2,cx,0," dwords\n"
endif ;HYBERDEBUG_R GE 3

    rep movs     es:dword ptr [edi],gs:dword ptr [esi]     ; copy data


nocopy:
;-----------------------------------------------------------------------------
; update addresses and count, loop if more to read
;-----------------------------------------------------------------------------
        sub      secct,ebx             ; decrement sector count
        jz       short unh_readok      ; if zero, we're done
        add      sect,ebx              ; increment sector address
        shl      ebx,9                 ; ebx = byte length
        add      dest,ebx              ; increment storage address
        jmp      unh_readloop

;-----------------------------------------------------------------------------
; Exit
;-----------------------------------------------------------------------------
unh_readok:
        xor      ax,ax                 ; set no error rc
unh_readexit:

if HYBERDEBUG_R GE 1
        dbgmsg   0,"unh_read done, ax=",2,ax,0,"\n"
endif ;HYBERDEBUG_R GE 1

        LeaveProc
        RETC                            ; return with ax = rc
;-----------------------------------------------------------------------------
; error return code setups
;-----------------------------------------------------------------------------
erri13w:
        mov      ah,03h
        jmp      short unh_readexit
errc2b:
        mov      ax,0401h
        jmp      short unh_readexit

EndProc unh_read

;********************* START OF SPECIFICATIONS *********************
;
; SUBROUTINE NAME: _MungeES
;
; DESCRIPTIVE NAME: Modify ES so that the full 4-Gbyte is accessible
;
; FUNCTION:     Plays a trick on the CPU by munging the real-mode
;               limit register so that we can blow the 1-meg limit
;
; ENTRY POINT:  _MungeES
;    LINKAGE:   call (near) from loader
;
; INPUT:        SS:SP -> the return address
;
; EXIT-NORMAL:   return to loader
;
; EXIT-ERROR:    None
;
; EFFECTS:       ES will have a 4G limit in the CPUs internal cache
;
; MESSAGES:
;
; INTERNAL REFERENCES:
;    ROUTINES:
;
; EXTERNAL REFERENCES:
;    ROUTINES:
;

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

gdtptr          dw      16,0,0

Procedure _MungeES
    assume ds:nothing,es:nothing,ss:DGROUP
        EnterProc

        xor     eax,eax
        mov     ax,cs
        shl     eax,4
        add     eax,offset mungegdt     ; eax = physical address of mungegdt
        mov     dword ptr ss:[gdtptr+2],eax
        mov     bx,offset gdtptr
        lgdt    fword ptr ss:[bx]
        mov     eax,cr0                 ;Enter protected mode & load'm up
        or      al,1
        mov     cr0,eax
        jmp     short $+2
        mov     bx,8
        mov     es,bx
        and     al,0FEh
        mov     cr0,eax


        LeaveProc
        ret
EndProc _MungeES



if HYBERDEBUG_R GE 2
Procedure pagetab     ;eax = lin addr
    assume ds:nothing,es:nothing,ss:DGROUP
        EnterProc
        push     eax
        push     ebx
        push     ecx

        mov      ebx,cr3          ; ebx = page dir base
        and      ebx,0FFFFF000h   ; clear page dir base flag bits
        mov      ecx,eax          ; ecx = linear address
        shr      eax,22           ; eax = page dir index
        dbgmsg   0,"%",4,ecx,0," pd[",2,ax,0,"] %%"
        shl      eax,2
        add      eax,ebx          ; eax = page dir entry addr
        mov      ebx,es:[eax]     ; ebx = page dir entry
        dbgmsg   4,eax,0," = ",4,ebx ; display page dir entry
        mov      eax,ecx
        shr      eax,12
        and      eax,03FFh        ; eax = page table index
        dbgmsg   0," pt[",2,ax,0,"] %%"; display page table index
        and      ebx,0FFFFF000h   ; clear page dir entry flag bits
        shl      eax,2
        add      eax,ebx          ; eax = page table entry addr
        mov      ebx,es:[eax]     ; ebx = page table entry
        dbgmsg   4,eax,0," = ",4,ebx,0,"\n"; display page table entry
        and      ebx,0FFFFF000h   ; clear page table entry flag bits
        and      ecx,000000FFFh   ; ecx = offset in page
        add      ebx,ecx          ; ebx = physical address
        mov      eax,ebx
        call     stgdisp          ; display storage contents

        pop      ecx
        pop      ebx
        pop      eax


        LeaveProc
        ret
EndProc pagetab

Procedure stgdisp     ;eax = address
    assume ds:nothing,es:nothing,ss:DGROUP
        EnterProc

        push     eax
        push     ebx
        push     ecx

        dbgmsg   0,"%%",4,eax,0," ="  ; display physical address
        mov      cx,8
stgloop:
        mov      ebx,es:[eax]
        add      eax,4
        dbgmsg   0," ",4,ebx          ; display dword of physical storage
        loop     stgloop

        dbgmsg   0,"\n"

        pop      ecx
        pop      ebx
        pop      eax

        LeaveProc
        ret
EndProc stgdisp
endif ;HYBERDEBUG_R GE 2


Procedure unh_rst,near
    assume ds:nothing, es:nothing, fs:nothing, gs:nothing, ss:nothing
        EnterProc

        mov      cx,sp

if HYBERDEBUG_R GE 3
        dbgmsg   0,"in unh_rst ss:sp=",2,ss,0,":",2,cx,0," edi=",4,edi,0,"\n"
endif ;HYBERDEBUG_R GE 3

        call    _MungeES
        mov     edx,edi

        cli

;-----------------------------------------------------------------------------
; Restore CR3; create page dir entry 0 to map 1st 4 Meg linear=physical
;-----------------------------------------------------------------------------
        mov     eax, es:[edx].hih_cr3  ; set cr3 (pdbr)
        mov     cr3, eax

if HYBERDEBUG_R GE 2
        dbgmsg   0,"cr3=",4,eax,0,"\n"
endif ;HYBERDEBUG_R GE 2

        mov      ebx, es:[edx].hih_cr3 ; ebx = page dir base
        and      ebx, 0FFFFF000h       ; clear flag bits
                                       ; eax = phys mem entry
        mov      eax, es:[ebx]+(HYBERPHYSMEM SHR (PAGEDIRSHIFT-2))
        mov      es:[ebx],eax          ; copy into dir entry 0

if HYBERDEBUG_R GE 2
        dbgmsg   0,"pdbr=",4,ebx,0," new pd[0]=",4,eax,0,"\n"
endif ;HYBERDEBUG_R GE 2

        and      eax,0FFFFF000h        ; clear page dir entry flags
if HYBERDEBUG_R GE 2
        call     stgdisp               ; display start of page table
endif ;HYBERDEBUG_R GE 2

;-----------------------------------------------------------------------------
; Create 2 temp GDT entries - code / data for this page
;-----------------------------------------------------------------------------
;       xor     ebx,ebx
        movzx   ebx, word ptr es:hyberInfoHdr_s.hih_GDTFrame[edx]
        shl     ebx, PAGESHIFT         ; ebx points to GDT base
        mov     ax,cs
        shl     eax,20                 ; gdt[1] base from cs
        mov     ax,000Fh               ; gdt[1] limit is 64 KB - 1
        mov     es:[ebx+TEMPGDT+0],eax ; store gdt[1] base[15:0] lim[15:0]
        xor     eax,eax
        mov     ax,cs
        shr     eax,12                 ; gdt[1] base from cs
        or      eax,00809B00h          ; page granular, 16 bit exec/read flags
        mov     es:[ebx+TEMPGDT+4],eax ; store gdt[1] flags base[23:16]
        mov     ax,ss
        shl     eax,20                 ; gdt[2] base from ss
        mov     ax,000Fh               ; gdt[2] limit is 64 KB - 1
        mov     es:[ebx+TEMPGDT+8],eax ; store gdt[1] base[15:0] lim[15:0]
        xor     eax,eax
        mov     ax,ss
        shr     eax,12                 ; gdt[2] base from ss
        or      eax,00809300h          ; byte granular, 16 bit read/write flags
        mov     es:[ebx+TEMPGDT+12],eax; store gdt[2] flags base[23:16]

if HYBERDEBUG_R GE 2
        dbgmsg   0,"GDT:\n"            ; display new GDT
        mov      eax,ebx
        call     stgdisp
endif ;HYBERDEBUG_R GE 2

;-----------------------------------------------------------------------------
; Issue lgdt & lidt
;-----------------------------------------------------------------------------

if HYBERDEBUG_R GE 2
        mov      bx,es:[edx].hih_gdt   ; display hih_gdt
        mov      eax,dword ptr es:[edx].hih_gdt+2

        dbgmsg   0,"hih_gdt=",2,bx,4,eax,0,"\n"

        add      eax,TEMPGDT
        call     pagetab               ; display * hih_gdt

        mov      bx,es:[edx].hih_idt   ; display hih_idt
        mov      eax,dword ptr es:[edx].hih_idt+2

        dbgmsg   0,"hih_idt=",2,bx,4,eax,0,"\n"

        call     pagetab               ; display * hih_idt
endif ;HYBERDEBUG_R GE 2

        mov     bx,es:[edx].hih_ds     ; bx = saved ds (flat)

        xor     ecx,ecx                ; calculate new tkssbase for use
        mov     cx,ss                  ;   once we're back in protect mode
        shl     ecx,4                  ; ecx = new tkssbase

        mov     edi,edx                ; unmunge es
        shr     edx,4
        mov     es,dx                  ; es = infohdr segment

        dbgmsg   0,"Setting cr0=",4,es:hih_cr0,0,"\n"

        db      MI_OPERANDSIZE         ; issue lgdt
        lgdt    fword ptr es:hih_gdt   ; kernel gdt (phys addr) ? s/b linear ?
;       outchar "A"


        db      MI_OPERANDSIZE         ; issue lidt
        lidt    fword ptr es:hih_idt
;       outchar "B"


;-----------------------------------------------------------------------------
; Switch to protect mode with paging enabled, load cs and ss selectors
;-----------------------------------------------------------------------------
        mov     eax, es:hih_cr0        ; set cr0
        mov     cr0, eax               ; enable prot mode & paging
        jmp     short $+2              ; clear prefetch queue
;       outchar "C"

                                       ; reload selectors
        mov     ax,TEMPGDT+8           ; new ss selector is gdt[2]
        mov     ss,ax
;       outchar "D"

        push    TEMPGDT                ; new cs selector is gdt[1]
        push    offset DGROUP:next_instruction
;       outchar "E"
        retf                           ; this is the easiest way to load cs
;       outchar "Z"

next_instruction:
;       outchar "F"
        mov     ds,bx                  ; reload other selectors to flat
        mov     es,bx
        mov     fs,bx
        mov     gs,bx
;       outchar "G"

        mov     eax, ds:hyberInfoHdr_s.hih_ptkssb[edi]
;       outchar "H"
        mov     ds:[eax],ecx           ; set our tkssbase
;       outchar "I"

;-----------------------------------------------------------------------------
; Reload various registers to saved values
;-----------------------------------------------------------------------------
        mov     ebx, [edi].hih_flags
        push    ebx
        popfd                                           ; interrupts disabled

        ; cr4 if necessary
        test    [edi].hih_hyberflags, HF_LOADCR4
        jz      short noCR4
        mov     ebx, [edi].hih_cr4
;       mov     cr4, ebx               ; next line actually is this instr.
        db      0Fh,22h,0E3h
noCR4:

        ; pic masks
        mov     al, [edi].hih_pic1mask
        out     PIC1_PORT1, al
        DELAY
        mov     al, [edi].hih_pic2mask
        out     PIC2_PORT1, al

        ; rtc status
        mov     al, 0ah                         ; status reg a
        out     RTCCTL, al
        DELAY
        mov     al, [edi].hih_rtca
        out     RTCDTA, al
        DELAY
        mov     al, 0bh                         ; status reg b
        out     RTCCTL, al
        DELAY
        mov     al, [edi].hih_rtcb
        out     RTCDTA, al
        DELAY
        mov     al, 0ch
        out     RTCCTL, al
        DELAY
        in      al, RTCDTA
        DELAY

        ; comm state - com1
        mov     dx, COM1_PORT + COM_LCR         ; line control reg
        mov     al, 80h
        out     dx, al                          ; set DLAB
        lea     eax, [edi].hih_com1
        mov     eax, [eax]                      ; fetch first word
        mov     dx, COM1_PORT + COM_DLM         ; divisor latch
        out     dx, al
        shr     eax, 8
        mov     dx, COM1_PORT + COM_DLL         ; divisor latch
        out     dx, al
        mov     al, [edi].hih_com1.hc_lcr
        mov     dx, COM1_PORT + COM_LCR         ; line control
        out     dx, al
        shr     eax, 8
        mov     dx, COM1_PORT + COM_MCR         ; modem control
        out     dx, al
        shr     eax, 8
        mov     dx, COM1_PORT + COM_IEN         ; interrupt enable
        out     dx, al

        ; comm state - com2
        mov     dx, COM2_PORT + COM_LCR         ; line control reg
        mov     al, 80h
        out     dx, al                          ; set DLAB
        lea     eax, [edi].hih_com2
        mov     eax, [eax]                      ; fetch first word
        mov     dx, COM2_PORT + COM_DLM         ; divisor latch
        out     dx, al
        shr     eax, 8
        mov     dx, COM2_PORT + COM_DLL         ; divisor latch
        out     dx, al
        mov     al, [edi].hih_com2.hc_lcr
        mov     dx, COM2_PORT + COM_LCR         ; line control
        out     dx, al
        shr     eax, 8
        mov     dx, COM2_PORT + COM_MCR         ; modem control
        out     dx, al
        shr     eax, 8
        mov     dx, COM2_PORT + COM_IEN         ; interrupt enable
        out     dx, al

        ; we need to mark the kernel tss not busy
;       xor     eax, eax
        movzx   eax, word ptr [edi].hih_GDTFrame
        shl     eax, PAGESHIFT                  ; gdt phys addr
        add     ax, [edi].hih_tr
        mov     ebx, [eax+4]                    ; 2nd dword of tss desc
        and     ebx, not 200h                   ; busy bit
        mov     [eax+4], ebx
        mov     ax, [edi].hih_tr
        ltr     ax

        ; misc regs
        mov     gs, [edi].hih_gs
;       mov     gs, ax
        mov     fs, [edi].hih_fs
;       mov     fs, ax
;       lgdt    fword ptr [edi].hih_gdt         ; edi=InfoHdr phys addr
        mov     ax, [edi].hih_ldt
        lldt    ax

        mov     ebp, [edi].hih_ebp
        mov     ss, [edi].hih_ss
;       mov     ss, bx
        mov     esp, [edi].hih_esp

        mov     eax, [edi].hih_ptkssb           ; _TKSSBase
        mov     ebx, [edi].hih_tkssb
        mov     [eax],ebx

        ; restore the dpl in the ds selector    ; debug ?? whats going on ??
        xor     ebx, ebx
        mov     bx, ds
        add     ebx, [edi].hih_gdt2             ; address of gdt
        ; TBR - save original in InfoHdr
        mov     eax, [ebx+4]                    ; fetch ds descriptor
        or      eax, 00006000h                  ; set dpl to 0
        mov     [ebx+4], eax

        push    0                               ; push cs and eip on stack
        push    [edi].hih_cs
        push    [edi].hih_reentry

;       int 3

        db      MI_OPERANDSIZE                  ; use ret to load cs and eip
        retf                                    ; of reentry to hybernat.c

        LeaveProc
EndProc unh_rst

copyend:

_TEXT   ends

        end
