;       SCCSID = src/cmd/createdd/rasint15.asm, rdmp, r131, 7.237 91/05/19
;       SCCSID = @(#)rasint15.asm       12.1 88/03/18
        PAGE    ,132  ; (CTRL-OH)   IBM PC PRINTER CONDENSED MODE
;*  @001SM SEM 06/09/94 Defect #85991 rewrite for performance, size, debug
;*                      capabilities, clarity of code. Share common code, data
;*                      with floppy.
;*                      Change way memory is copied from above 1 meg.
;*                        Switch into protectmode via protmode 1x, then
;*                        dont need to invoke protmode anymore.
;*                      Protmode also used to reboot system when bp=1
;*                      Protmode used to be called bring_it_down then
;*                         it was renamed to bring_it_down16.
;*                      Changes to support new way to copy memory are
;*                       marked with @001SM. Way in which delays were made
;*                       was also changed.
;* @002SM SEM 06/22/94 Remove ext_bios, not needed.
;*                     Dont call enable_a20, it was never disabled
;*                     Commented out enable_a20, disable_a20.
;* @008wm wdm 11/01/95  Defect 140892 - PC750 doesn't reboot after complete dump
;*---------------------------------------------------------------------------------
.286c
debugsht   equ    0               ; @008wm - if desire short output

CODE    SEGMENT   PUBLIC 'CODE'
        ASSUME CS:CODE,DS:CODE,ES:CODE,SS:nothing

public          protmode
;public          ext_bios              ; @002SM

extrn           DoDelayO:near          ; @001SM
extrn           beep:near              ; @008wm
extrn           debugprt:near          ; @008wm
extrn           key_waitx:near         ; @008wm
extrn           idcnt:byte             ; @008wm
extrn           rebtflag:byte          ; @008wm


;********************** START OF SPECIFICATIONS **********************
;*                                                                   *
;* SUBROUTINE NAME: protmode                                         *
;*                                                                   *
;* DESCRIPTIVE NAME: MOVE EXTENDED MEMORY TO REAL MODE MEMORY        *
;*                                                                   *
;* FUNCTION:                                                         *
;*         THIS MODULE WILL JUMP INTO 386 PROTECT MODE, AND CREATE   *
;*         FLAT SELECTORS TO ACCESS ALL OF MEMORY.                   *
;*         Then it will either reboot by causing a triple fault      *
;*         or it will return to real mode.                           *
;*         Once in real mode, will be able to address all of memory  *
;*         via FS selector which now has a cached limit of 4 gig     *
;*         The trick here is that FS is set to a selector of 4 gig   *
;*         in protect mode. When switched back to real mode, only    *
;*         ds,es are reset back to 64k-1 limits. fs remains at 4gig. *
;*         NOTE: must make sure the a20 line is enable when go back  *
;*               real mode, otherwise wont see memory with           *
;*               bit 20 = 1.                                         *
;*Input:   BP : operation to perform. 0=realmode  , 1=reboot    *
;*********************** END OF SPECIFICATIONS ***********************
;
; The following code was assembled/linked/exe2bin'd over and over again to
; get by the strange things that happen in MASM when the .386p directive is
; given.  Much of the code such as the "fixme" label looks extraneous, but this
; module will not work any other way.  In order for this code to be linked to
; the rest of the stand-alone dump code, it must be "COM" file compatible.
; MASM generates 32-bit fixups when in 386p mode and all offsets must be
; 32-bit even in a use16 segment.
;      The linker is apparently suppressing an error when it tries to resolve
; the 32-bit fixups because any "DW label" statements are being resolved to
; random numbers.  I got around this by doing some ugly self-modifying code.
; Turbo linker from Borland refuses to link this file because of the 32-bit
; fixups; luckily, MS linker ignores the errors.
;
; Larry Bank 2/7/92
;
;KBD_STATUS_PORT EQU     64H     ;@002SM
;PORT_A          EQU     60H     ;@002SM
;BUF_FULL        EQU     2H      ;@002SM
;ctlporta        equ     092h    ;System Control Port "A" @002SM
;alt_supp_A20    equ     002h    ;@002SM
;
;  Structures
;
desc    struc
 lim_15_0       dw      0       ;16 bits
 bas_15_0       dw      0       ;16 bits
 bas_23_16      db      0       ;8  bits
 access         db      0       ;8  bits
 gran           db      0       ;4  bits, lim_19_16 4 bits
 bas_31_24      db      0
desc    ends

protmode        proc    near
                jmp     doit
;
;  my GDT has 3 descriptors:
;   08h: Code Segment - Starts at real CS
;   10h: Data Segment - My flat 4GB data segment starting at 0
;   18h: Data Segment - used for REAL mode compatibility
;
NullDesc: desc    <>
CodeDesc: desc    <0FFFFh,00000h,000h,09Bh,00Fh,000h>
DataDesc: desc    <0FFFFh,00000h,000h,092h,0cFh,000h>
realdata: desc    <0FFFFh,00000h,000h,093h,000h,000h>

gdtptr  label   fword
        dw      0001Fh          ; 4 8-byte selectors (null,code,data, real)
gdt32o  dd      0               ; Self-modify this dword later with offset
doit:   push    es
        push    ax
        push    bx
        push    cx              ; save registers which I step on
        push    dx
        mov     ax,cs           ; set DS=CS
        mov     ds,ax
        call    fixme2          ; find out where we are
fixme2: pop     bx              ; get the current IP in BX
        mov     cx,bx           ; keep it here for later
        add     bx,reality-fixme2    ; get the relative distance from here
        mov     [bx-2],ax       ; save CS here for later
        mov     [bx-4],bx       ; and save the offset since MASM screws it up
        mov     bx,cx           ; get "FIXME2" offset back
        add     bx,prot386-fixme2    ; get the relative distance from here
        mov     [bx-4],bx       ; save the prot386 true offset here
        mov     al,ah           ;Hi-order segment to lo
.386p
        shr     al,4            ;Position for bits 23-16 location
        mov     bx,cx           ; get "FIXME2" offset back
        add     bx,NullDesc-fixme2    ; get the relative distance from here
;        mov     codedesc.bas_23_16,al
        mov     [bx+12],al
;        mov     realdata.bas_23_16,al
        mov     [bx+28],al
        xor     eax,eax         ;Clear
        mov     ax,cs           ;Start again
        shl     eax,4           ;Linear address
;        mov     codedesc.bas_15_0,ax    ;Set base_15_0
        mov     [bx+10],ax
;        mov     realdata.bas_15_0,ax    ;Set base_15_0
        mov     [bx+26],ax
        shl     esi,16          ; move upper 16 bits of address high
        mov     si,di           ; get lower 16 bits of address

;       Calculate 32-bit offset of GDT.

        and     ecx,0ffffh      ; ecx has real offset to fixme2
;        add     eax,offset nulldesc     ;Setup Proper Pointer to the Segment
        add     ecx,nulldesc-fixme2 ; point to null desc
        add     eax,ecx         ; EAX has flat pointer to nulldesc now
;        mov     dword ptr gdt32o,eax
        mov     [ecx+34],eax    ; store the true GDT pointer
        add     cx,32           ; now CX points to gdtptr
        add     bx,fixme4-NullDesc    ; get pointer to lgdt instruction offset
        mov     [bx],cx         ; fix lgdt instruction
;
IF  debugsht                      ; @008wm
        call    beep              ; @008wm for debug
        mov     cs:idcnt,'a'      ; @008wm
        call    debugprt          ; @008wm
        call    key_waitx         ; @008wm - in hdrive.asm, not floppy.asm
ENDIF                             ; @008wm
        cli                       ; @008wm - disable interrupts
        cmp    rebtflag,0         ; @008wm switch to real mode or reboot?
        je     skiprebt           ; @008wm bp = 0 --> realmode
; @008wm reboot through keyboard.  If it doesn't work, try triple fault in protect mode.
        mov    bl, 0FEh           ; @008wm - system reset command
        call   keyboard_cmd       ; @008wm - returning says it did not work.
        cli                       ; @008wm - returns enabled, so disable again
;  Put the Processor into Protect Mode

; This next call instruction is critical because the lgdt instruction
; has already been prefetched in some cases, and the modification will not
; be made to the instruction once it's in the prefetch Q; this fact is
; documented in the i486 programmer's reference page 12-2.
;
skiprebt:                         ; @008wm
        call    bogus             ; flush prefetch to make sure lgdt has new data
;        lgdt    gdtptr           ; load my GDT pointer
        db      0fh,01,16h
fixme4: dw      0

        mov     eax,CR0         ; enable protect mode
        or      al,1
        mov     CR0,eax
        call    bogus           ; @008wm - flush prefetch buffer
;       jmp far ptr prot386     ; flush prefetch Q, jump into hyper-space
        db      0EAh            ; Make it a FAR JMP
fixme3: dw      prot386         ; jmp to 8:prot386
        dw      8h
;
; This next section is what gets executed in 386 protect mode
;
prot386: cmp    rebtflag,0      ; @008wm - switch to real mode or reboot?
        je      doreal          ; @008wm - bp = 0 --> realmode; else force reboot
        xor     ax,ax
        mov     ds,ax
        mov     ds:[si],ax      ; @08wm - reboot here - caused by trap d (null selector)
;
doreal:                         ; @008wm
        mov     ax,00010h       ; point to my flat selector   @001SM
        mov     fs,ax           ; when switch to real mode, fs will address 4gig @001SM
;@001SM the code to copy memory from dumpflat address to dumpseg is now in
;@001SM copymem in hdrive.asm. The copy can be performed in real mode to fs:
;@001SM only need to execute the above 1 time.

;   Reload selectors with 64k limited segments so that real mode can work
;
        mov     ax,00018h       ; selector #3 has a 64k limit and normal base
        mov     ds,ax
        mov     es,ax
;   Make sure fs stays set to 10h which has 4gig limit, allows to address all
;   memory in real mode @001SM
        mov     eax,CR0
        and     eax,07FFFFFFEh  ; disable page  & protect modes,
        mov     CR0,eax         ; update CR0,
        mov     CR3,eax         ; and purge TLB.

;       jmp far ptr reality     ; flush prefetch and return to reality
        db      0EAh            ; Make it a FAR JMP
        dw      reality
fixme:  dw      0
.286
reality: mov    ax,cs           ; reload DS with a REAL value
        mov     ds,ax
        sti                     ; interrupts can resume
IF  debugsht                      ; @008wm
        mov     cs:idcnt,'b'      ; @008wm
        call    debugprt          ; @008wm
        call    key_waitx         ; @008wm
ENDIF
        pop     dx
        pop     cx
        pop     bx              ; restore the registers I stepped on
        pop     ax
        pop     es
        ret                     ; return to caller
protmode endp
;
; used to make sure the prefetch Q gets flushed
;
bogus   proc    near
        ret
bogus   endp


;---------------------------------------------------------------
;    KEYBOARD_CMD     @008wm - added for PC750 reset.  The 750 would not
;                              triple fault and reset, so we use this.
;       Send a command in register BL to the keyboard controller (port 64h).
;
;       If the routine times out due to the buffer remaining
;       full, ah is non-zero.
;
;       Called with:    bl = command byte
;                       ds = cs
;
;       Returns:        if ah = 0, successful
;                       if ah = 1, failed
;
;       Regs Used:      ax, cx


keyboard_cmd    proc    near
        mov     cx,0600h           ; loop decrement for timeout
cmd_wait:
        in      al, 64h            ; get controller status
        call DoDelayO
        test    al, 2              ; is input buffer full?
        jz      cmd_send           ; ready to accept command ?
        loop    cmd_wait           ; jump if not
                                   ; fall through, still busy
        jmp     cmd_error

cmd_send:                          ; send command byte
        mov     al, bl
        out     64h, al            ; send command
        call DoDelayO

        mov     cx,0600h           ; loop decrement for timeout
cmd_accept:
        in      al, 64h            ; get controller status
        call DoDelayO
        test    al, 2              ; is input buffer full?
        jz      cmd_ok             ; jump if command accepted
        loop    cmd_accept         ; try again
                                   ; fall through, still busy
cmd_error:
        mov     ah, 1              ; return status - failed
        jmp     cmd_exit
cmd_ok:
        xor     ah, ah             ; return status ok
cmd_exit:
        ret
keyboard_cmd    endp


CODE    ENDS
        END
