I'd like to implement Unreal mode (access all 4GB memory) with NASM in DOS. I've found a TASM implementation:
.386p
RealSeg Segment Para Public Use16
Assume cs:RealSeg, ds:RalSeg
Real_Start:
cli
push cs
pop ds
mov ax,3
int 10h
mov ah,9
lea dx,Ex5_Str
int 21h
mov eax,cs
shl eax,4
add GDT_Base,eax
lgdt GDT_Value
mov eax,CR0
or al,1
mov CR0,eax
jmp $+2
mov ax,Real4G
mov es,ax
mov eax,CR0
and al,not 1
mov CR0,eax
jmp $+2
mov eax,'AAAA'
mov ecx,40
mov edi,0b8000h
L1:
mov es:[edi],eax
add edi,4
loop L1
mov ah,4ch
int 21h
Exit:
mov ah,9
int 21h
mov ah,4ch
int 21h
Ex5_Str db 10,13,10,13
db '4GB memory OK',10,13,36
RelSeg Ends
StackSeg Segment Stack Use16
db 400h dup (?)
StackSeg Ends
End Real_Start
Or here is a FASM implementation:
struc GDT_STR
s0_15 dw ?
b0_15 dw ?
b16_23 db ?
flags db ?
access db ?
b24_31 db ?
ENDS
SEGMENT CODE16 USE16 PUBLIC
ASSUME CS:CODE16
; GDT definitions
gdt_start dw gdt_size
gdt_ptr dd 0
dummy_descriptor GDT_STR <0,0,0,0,0,0>
code16_descriptor GDT_STR <0ffffh,0,0,9ah,0,0> ; 64k 16-bit code
data32_descriptor GDT_STR <0ffffh,0,0,92h,0cfh,0> ; 4GB 32-bit data, 92h = 10010010b = Presetn , DPL 00, No System, Data Read/Write
gdt_size = $-(dummy_descriptor)
dummy_idx = 0h ; dummy selector
code16_idx = 08h ; offset of 16-bit code segment in GDT
data32_idx = 10h ; offset of 32-bit data segment in GDT
PUBLIC _EnterUnreal
PROC _EnterUnreal FAR
PUSHAD
PUSH DS
PUSH CS
POP DS
mov ax,CODE16 ; get 16-bit code segment into AX
shl eax,4 ; make a physical address
mov [ds:code16_descriptor.b0_15],ax ; store it in the dscr
shr eax,8
mov [ds:code16_descriptor.b16_23],ah
XOR eax,eax
mov [ds:data32_descriptor.b0_15],ax ; store it in the dscr
mov [ds:data32_descriptor.b16_23],ah
; Set gdt ptr
xor eax,eax
mov ax,CODE16
shl eax,4
add ax,offset dummy_descriptor
mov [gdt_ptr],eax
cli
mov bx,offset gdt_start
lgdt [bx]
mov eax,cr0
or al,1
mov cr0,eax
mov ax,data32_idx
mov fs,ax
mov eax,cr0
and al,not 1
mov cr0,eax
MOV AX,0
MOV FS,AX
POP DS
POPAD
RETF
ENDP
I'd like to use NASM, but it is very difficult to translate below listed instructions to NASM:
Do anybody has any working NASM Unreal mode implementation? Or can somebody help to translate either of the above examples to NASM?
You can create the GDT_STR
structure in NASM this way:
struc GDT_STR
.s0_15: resw 1
.b0_15: resw 1
.b16_23: resb 1
.flags: resb 1
.access: resb 1
.b24_31: resb 1
endstruc
and then you instantiate these structures like this:
dummy_descriptor:
istruc GDT_STR
at .s0_15, dw 0
at .b0_15, dw 0
at .b16_23, db 0
at .flags, db 0
at .access, db 0
at .b24_31, db 0
iend
code32_descriptor:
istruc GDT_STR
at .s0_15, dw 0xffff
at .b0_15, dw 0
at .b16_23, db 0
at .flags, db 0x92
at .access, db 0xcf
at .b24_31, db 0
iend
The jmp $+2
jumps to the next instruction in 16-bit code. In the context of the unreal mode code the relative jump to the next instruction clears the instruction prefetch queue. You can replace it with something like:
jmp next1 ; Flush the instruction prefetch queue
next1:
The code uses NASM to generate an obj
file that can be linked into an executable. See the comments at the top of the code for instructions on assembling and linking.
; Assemble with
; nasm -f obj unreal.asm -o unreal.obj
;
; Link with Watcom Linker to unreal.exe
; wlink format dos file unreal.obj
;
; Or link with MS 16-bit segmented linker
; link16 unreal.obj;
;
; Or link with TLink (Turbo link)
; tlink unreal.obj
; Define GDT_STR - a GDT descriptor entry
struc GDT_STR
.s0_15: resw 1
.b0_15: resw 1
.b16_23: resb 1
.flags: resb 1
.access: resb 1
.b24_31: resb 1
endstruc
bits 16
segment data
align 4
gdt:
FLAT_SEL equ .flat - .start
.start:
.null:
istruc GDT_STR
at .s0_15, dw 0
at .b0_15, dw 0
at .b16_23, db 0
at .flags, db 0
at .access, db 0
at .b24_31, db 0
iend
.flat:
istruc GDT_STR
at .s0_15, dw 0xffff
at .b0_15, dw 0
at .b16_23, db 0
at .flags, db 0x92
at .access, db 0xcf
at .b24_31, db 0
iend
.end:
.gdtr:
dw .end - .start - 1
; limit (Size of GDT - 1)
dd .start ; base of GDT. Needs to be fixed up at runtime
in_pmode_str: db "Processor already in protected mode - exiting", 0x0a, 0x0d, "$"
section code
; ..start is a special label for the DOS entry point
..start:
; Save initial EFLAGS state
pushfd
cli
; Initialize DS to our DOS DATA segment (needed for EXEs)
; SS:SP will be set by the DOS EXE loader to our stack segment
mov ax, data
mov ds, ax
check_pmode:
smsw ax
test ax, 0x1 ; Check if we are already in protected mode
; This may be the case if we are in a VM8086 task.
; EMM386 and other expanded memory manager often
; run DOS in a VM8086 task. DOS extenders will have
; the same effect
jz not_prot_mode ; If not in protected mode proceed to switch
mov dx, in_pmode_str ; otherwise print an error and exit back to DOS
mov ah, 0x9
int 0x21 ; Print Error
jmp exit ; Exit program
not_prot_mode:
; Apply a fixup to the GDTR base to convert to a linear address
mov eax, ds
shl eax, 4
add [gdt.gdtr+2], eax
lgdt [gdt.gdtr] ; Load our GDT
mov cx, ds ; Save DS so it can be restored
mov eax, cr0
or al, 1
mov cr0, eax ; Set protected mode flag
jmp .next1 ; Flush the instruction pefetch queue
.next1:
; In 16-bit protected mode
; Since we aren't changing CS
; we don't need to enter 32-bit protected mode
mov bx, FLAT_SEL
mov ss, bx
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx
and al, ~1
mov cr0, eax ; Unset protected mode flag
jmp .next2 ; Flush the instruction pefetch queue
.next2:
; Unreal mode here
; Restore SS=DS=ES
mov ss, cx
mov ds, cx
mov es, cx
; Print UNR to the screen using a flat 4GiB selector
; This code won't work in regular real mode
xor ax, ax
mov fs, ax
mov word fs:[dword 0xb8000+80*2*3+0], 0x57<<8 | 'U'
mov word fs:[dword 0xb8000+80*2*3+2], 0x57<<8 | 'N'
mov word fs:[dword 0xb8000+80*2*3+4], 0x57<<8 | 'R'
exit:
; Restore initial EFLAGS state
popfd
; DOS exit
mov ax, 0x4c00
int 0x21
segment stack stack
resb 1024
Note: Getting into unreal mode won't work if the processor is already in protected mode (ie executing as a v8086 task). This can be the case with EMM386, DOS extenders and code that relies on DPMI.