;       Flat real mode demonstration program

.386

code    segment use16

        assume  cs:code
        org     100h

example proc    ; Example code
        
        call    FLAT_inst               ; Enable 4Gb address space

        mov     ax,3                    ; Colour text mode
        int     10h

        xor     ax,ax                   ; Base ES <- 000[0000]0h
        mov     es,ax
        mov     ah,1fh                  ; Colour = White on blue
        mov     si,offset message
        mov     edi,000b8000h           ; EDI <- top left screen address
        mov     cx,80                   ; Write one line
 l1:      lodsb                         ; Get a char
          stos  word ptr es:[edi]       ; Store char + attrib
        loop    l1

        call    FLAT_dest               ; <- Very Important!!!

        ret

message db      '  Message written using direct screen writing' 
        db      ' & 32-bit offsets in REAL-mode!!   '

example endp


FLAT_inst proc  ; Installs FLAT_tsr
        
.386p                                   ; SMSW is a priviledged instruction
        smsw    ax                      ; Check for real mode
.386
        test    al,1
        jnz     short V86
        pushf                           ; Save flags & DS register
        push    ds
        cli
        xor     eax,eax                 ; Get IRQ5 vector & Set FLAT_tsr
        mov     ds,ax
        mov     ebx,ds:[34h]
        mov     cs:old_IRQ5,ebx
        mov     word ptr ds:[34h],offset FLAT_tsr
        mov     ax,cs
        mov     ds:[36h],ax
        shl     eax,4                   ; Build Global Descriptor Table
        add     dword ptr cs:GDTR[2],eax
        pop     ds                      ; Restore DS register & flags
        popf
        ret

V86:    mov     ah,09h                  ; Write message
        mov     dx,offset V86_msg
        push    cs
        pop     ds
        int     21h
        mov     ax,4C01h                ; Terminate with error code 1
        int     21h

V86_msg         db      'Cannot run in a virtual environment!',10,'$'

FLAT_inst endp

FLAT_dest proc  ; Destalls FLAT_tsr

        push    ds                      ; Save DS register
        xor     ax,ax                   ; Restore old IRQ5 vector
        mov     ds,ax
        mov     eax,cs:old_IRQ5
        mov     ds:[34h],eax
        pop     ds                      ; Restore DS register
        ret

FLAT_dest endp

        align   8
GDT             dd      0,0             ; 1st entry needs to be 'null'

FLAT_desc       dw      0ffffh          ; Limit (bit 0..15)
                db      ?,?,?           ; Base (bit 0..23)
                db      92h             ; Access rights
                db      0cfh            ; Page granularity + Limit(16..19)
                db      ?               ; Base (bit 24..31)

FLAT_sel        equ     FLAT_desc - GDT

GDTR            dw      $ - GDT         ; Limit (16 bytes)
                dd      offset GDT      ; Offset within current segment...
                dw      0

old_IRQ5        dd      0
last_Exc_13     dd      0
IRQ5_flag       db      0

FLAT_tsr proc

        test    cs:IRQ5_flag,1          ; Exception within IRQ5 handler?
        jnz     short Exc_13
        push    ax                      ; Ask PIC if IRQ5 In_Service
        mov     al,0Bh
        out     20h,al
        jmp     $+2
        in      al,20h
        test    al,20h
        pop     ax
        jz      short Exc_13

IRQ5:   mov     cs:IRQ5_flag,1          ; Call old IRQ5 handler
        pushf
        call    dword ptr cs:old_IRQ5
        mov     cs:IRQ5_flag,0
        iret

Exc_13: push    eax                     ; Save accumulator

        mov     eax,ss:esp[4]           ; Get address of SOE
        cmp     eax,cs:last_Exc_13      ; Same as last time?
        je      short SOE
        mov     cs:last_Exc_13,eax
.386p
        lgdt    qword ptr cs:GDTR       ; Load GDT Register
        
        push    gs fs es ds bx          ; Save registers

        mov     eax,CR0
        or      al,1                    ; Enter Protected mode
        mov     CR0,eax
        
        mov     bx,FLAT_sel             ; Load 4Gb limits
        mov     ds,bx
        mov     es,bx
        mov     fs,bx
        mov     gs,bx

        and     al,not 1                ; Back to Real mode
        mov     CR0,eax
.386        
        pop     bx ds es fs gs          ; Restore registers
        pop     eax                     ; Restore accumulator
        iret                            ; Done

SOE:    call    FLAT_dest               ; Remove FLAT_tsr
        mov     ah,0fh                  ; Clear screen
        int     10h
        mov     ah,00h
        int     10h
        mov     ah,09h                  ; Write message
        mov     dx,offset SOE_msg
        push    cs
        pop     ds
        int     21h
        mov     ax,4C0Dh                ; Terminate with error code 13
        int     21h

SOE_msg         db      'Segment Overrun Exception!',10,'$'

FLAT_tsr endp

code    ends
        end     example
