comment %

support file for putting the CPU into flat real mode
support file for gate A20 switching

%

;----------------------------------------------------------------------------
;                      MASSiVE Flat Real mode routines
;                               (Version 1.0B)
;                                    by
;                               NiX/MASSiVE
;                            Butterfly/MASSiVE
;
; You may use this sourcecode for free, we don't accept anything in return.
; But we would really like it if you greet us, or send your newest productions
; to us.
;
; This is a beta version, only providing you with the essential routines and
; some examples, just real plain flat-real-mode. More special things will be
; available in a next version.
;
; Read the DOC for more information.

.386P    ; 386 Code ofcourse...

;---------------------------------------------------------------------------
; Descriptors and stuff used for setting up FRM
mem32_GDT       dw      4 dup(0)

Code16GDT = 8
                dw      0ffffh,0ffh,9a00h,0
Data16GDT = 16
                dw      0ffffh,0ffh,9200h,0
Data32GDT = 24  
                dw      0ffffh,0ffh,9200h,8fh

GDTptr label fword
                dw      offset GDTptr-1-offset Mem32_GDT
                dd      offset Mem32_GDT ; Absolute adress GDTtable
                dw      0

;-------------------------------------------------------------------------
; This routine sets DS, ES, FS and GS to a 4Gigabyte limit.
; To do this it swaps to PM for a little while, this is not possible in V86
; mode. If computer is in V86 mode the carry will be set on return, and you
; are still in RM.
; Crashes segment registers!!!

Set4Gig:        mov     eax,cr0    ; Check for V86 mode
                ror     eax,1      ;
                jc      flat_error ; If carry set get out of here.

                mov     ax,cs      ; Set up GDT for this code segment
                mov     ds,ax
                movzx   eax,ax
                shl     eax,4
                add     dword ptr ds:GDTptr+2,eax
                lgdt    fword ptr ds:GDTptr

                mov     ax,cs
                and     eax,65535
                shl     eax,4
                mov     word ptr ds:Mem32_GDT[Code16GDT+2],ax
                mov     word ptr ds:Mem32_GDT[Data16GDT+2],ax
                ror     eax,16
                mov     byte ptr ds:Mem32_GDT[Code16GDT+4],al
                mov     byte ptr ds:Mem32_GDT[Data16GDT+4],al
                mov     byte ptr ds:Mem32_GDT[Code16GDT+7],ah
                mov     byte ptr ds:Mem32_GDT[Data16GDT+7],ah

                cli    ; No ints

; Beam us to PMODE!!
                mov     eax,cr0
                or      al,1
                mov     cr0,eax

                db      0eah               ; Far jump to PMODE label
                dw      offset Pmode
                dw      Code16GDT

; We're in PMODE!!
Pmode:          mov     ax,Data32GDT
                ;mov     ds,ax  ; Set them all to 4Gig
                ;mov     es,ax  ;leave ES and DS alone, cuz old 8088 programs
                mov     fs,ax   ;may rely on old memory wrapping techniques!
                mov     gs,ax
                
; Beaming out!
                mov     eax,cr0
                and     al,0feh
                mov     cr0,eax

                db      0eah           ; Far jump to Rmode label.
                dw      offset Rmode
                dw      Code

; Back in the real world!
Rmode:          clc  ; No carry, everything went OK

                sti  ; Ints Back on.

                mov     ax,cs            ; set CS=DS
                mov     ds,ax
                xor     ax,ax            ; Set segment register to 0
                mov     gs,ax            ;

Leave4Gig:      ret

flat_error:
                push cs
                pop ds
                mov     dx,offset V86Mess
                mov     ah,9
                int     21h
                mov     ax,4c03h                ; errorlevel 3=v86mode
                int     21h

V86mess         db      "Error: V86 Mode present!",CR,LF,"$"

;-----------------------------------------------------------------------------
; Enables the A20 adress line. This is needed to adress the memory above 1Mb.
; Do this at the start of your program and everytime another program has been
; active. In an interrupt routine this means you should do it everytime the
; routine is being called, don't worry, it doesn't takes much time.
EnableA20:
                mov     al,0D1h
                out     64h, al
                call    A20wait
                mov     al,0DFh
                out     60h, al
                call    A20wait
                mov     al,0FFh
                out     64h,al
                call    A20wait
                ret

A20Wait:        in      al,64h
                jmp     $+2
                and     al,2
                jnz     A20wait
                ret


;copies 0:0->512k to 2mb area.  define this address via declaration!!
;eax, ecx, esi, edi are destroyed.

put512high proc near


        mov     ecx, (512*1024) / 4
        mov     edi, DOS_START                ; copy to 2mb
        xor     esi, esi


        cli
copyhloop:
        mov     eax, gs:[esi]
        mov     gs:[edi], eax
        add     edi, 4
        add     esi, 4
        loop    copyhloop

        ; save SS:SP

        mov     ax, sp
        shl     eax, 16
        mov     ax, ss
        mov     gs:[edi], eax
        add     edi, 4
        mov     gs:[edi], bp

        sti
        ret
put512high endp

;copies 0:0->512k to 2mb area.  define this address via declaration!!
;eax, ecx, esi, edi are destroyed.
put512low proc near


        mov     ecx, (512*1024) / 4
        mov     esi, DOS_START                ; copy to 2mb
        xor     edi, edi


        cli
copylloop:
        mov     eax, gs:[esi]
        mov     gs:[edi], eax
        add     esi, 4
        add     edi, 4
        loop    copylloop

        sti
        ret
put512low endp

;---------------------------------------------------------------------------
;swaps 0:0->512 with 2mb area
;kills all upper 16 bits of all registers.

_swap512k macro
local swaploop

        shl     eax, 16                         ; save ax
        shl     ebx, 16                         ; save bx
        shl     edx, 16                         ; save dx
        mov     dx, cx                          ; save cx
        shl     ebp, 16                         ; save bp
        mov     bp, si                          ; save si
        mov     fs, di                          ; save di (fs is never used)

        mov     ecx, (512*1024) / 2
        mov     edi, DOS_START
        xor     esi, esi

        cli
swaploop:
        mov     ax, gs:[esi]
        mov     bx, gs:[edi]           
        mov     gs:[edi], ax
        mov     gs:[esi], bx          
        add     edi, 2
        add     esi, 2
        loop    swaploop



        mov     di, fs                          ; restore di
        mov     si, bp                          ; restore si
        shr     ebp, 16                         ; restore bp
        mov     cx, dx                          ; restore cx
        shr     edx, 16                         ; restore dx
        shr     ebx, 16                         ; restore bx
        shr     eax, 16                         ; restore ax

        sti

endm

;----------------------------
;check_himem
;checks if himem.sys is loaded, abort with error message if so.
;
;

check_himem proc near
        mov     ax, 4300h                       ; installation check
        int     2fh                             ; al=80h if installed.
        cmp     al, 80h    
        jz      himemerr
        ret
himemerr:       
        push    cs
        pop     ds
        mov     dx,offset himemerrmsg
        mov     ah, 9
        int     21h

        mov     ax, 4c02h                       ; errorlevel 2=himem
        int     21h

himemerrmsg     db      "Error: This program does not work with Himem.sys "
                db      "installed.",CR,LF,"Boot clean and try again!",CR,LF,"$"

check_himem endp
;--------------------------------------------------------------------------
check_2mb proc near
        mov     ax, 8800h
        int     15h                             ; returns extended size in k
        cmp     ax, 1*1024                      ; requires 1mb extended mem
        jb      mem_error
        ret
mem_error:
        push    cs
        pop     ds
        mov     dx,offset need2mbmsg
        mov     ah, 9
        int     21h

        mov     ax, 4c04h                       ; errorlevel 4=memsize
        int     21h

need2mbmsg     db      "Error: This program requiresd at least 2mb.",CR,LF,"$"
check_2mb endp


;--------------------------------------------------------------------------
;tweak reported memory size to 512k instead of 640
change_base_size proc near
        mov     word ptr gs:[413h],512
        ret
change_base_size endp

