Как реализовать режим Unreal с NASM (DOS)?

Я хотел бы реализовать режим Unreal (доступ ко всей памяти 4 ГБ) с NASM в DOS. Я нашел реализацию TASM:

.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

Или вот реализация FASM:

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

Я хотел бы использовать NASM, но очень сложно перевести приведенные ниже инструкции на NASM:

  • [ds:data32_descriptor.b0_15],ax
  • data32_descriptor GDT_STR <0ffffh,0,0,92h,0cfh,0>
  • $-(фиктивный_дескриптор)
  • jmp $+2
  • и т. д.
  • и т. д.

Есть ли у кого-нибудь работающая реализация режима NASM Unreal? Или может кто-нибудь помочь перевести любой из приведенных выше примеров на NASM?

jmp $+2 в NASM то же самое, что и в MASM/TASM и FASM. $-(dummy_descriptor) тоже самое, но вместо FASM = нужно equ в NASM. Как именно работает $ в NASM? . Что касается синтаксиса структуры, проверьте руководство NASM или просто сделайте это «вручную» без структур. nasm.us/doc/nasmdoc5.html#section-5.9
Peter Cordes 03.05.2024 00:56

Если вы расстроены, это jmp $+2 можно записать как db 0xEB 0x00. Я не особенно рекомендую это, но будет работать.

Joshua 03.05.2024 00:58

@Джошуа: Или jmp next_insn/next_insn:, чтобы сделать это с помощью ярлыка.

Peter Cordes 03.05.2024 00:59

Единственное, чего я не понимаю, так это зачем вам нужны эти инструкции jmp. Поскольку идея здесь состоит в том, чтобы загрузить сегменты с разблокированными смещениями и вернуться в реальный режим, я бы подумал, что вам не нужны инструкции jmp, поскольку мы хотим продолжать декодировать 16-битные инструкции, несмотря на переход на 32-битные. Если бы мы уже декодировали 32-битную версию, вывод ассемблера для mov ax,Real4G определенно был бы неверным, потому что он этого не знает.

Joshua 03.05.2024 01:03

Близкий прыжок в любом случае не перезагружает сегмент.

Jester 03.05.2024 03:14

@Jester Кажется, это «большой нереальный» режим только с 32-битным сегментом данных. Никакой регистр сегмента кода не требует перезагрузки. Была рекомендация Intel объявить конвейер недействительным, поэтому короткого перехода было достаточно. Я не знаю, актуально ли это для современных процессоров.

dimich 03.05.2024 06:01

Этот код создает программу DOS COM, которая переходит в реальный режим и выводит на дисплей, используя плоский дескриптор. pastebin.com/68W8cn0d . Обратите внимание, что это плохая реализация, поскольку внешнее прерывание или вызов BIOS/DOS могут выйти из нереального режима. Режим Unreal лучше всего использовать по требованию. Код также должен проверить, находитесь ли вы уже в защищенном режиме, вывести ошибку и выйти.

Michael Petch 03.05.2024 12:17
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
7
136
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете создать структуру GDT_STR в NASM следующим образом:

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

а затем вы создаете экземпляры этих структур следующим образом:

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

jmp $+2 переходит к следующей инструкции в 16-битном коде. В контексте кода нереального режима относительный переход к следующей инструкции очищает очередь предварительной выборки инструкций. Вы можете заменить его чем-то вроде:

    jmp next1                   ; Flush the instruction prefetch queue
next1:

Код использует NASM для создания файла obj, который можно связать в исполняемый файл. Инструкции по сборке и компоновке см. в комментариях вверху кода.

; 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

Примечание. Переход в нереальный режим не сработает, если процессор уже находится в защищенном режиме (т. е. выполняется как задача v8086). Это может быть в случае с EMM386, расширителями DOS и кодом, основанным на DPMI.

Я думал, что есть способ вернуть реальный режим из EMM386; таким образом, вы действительно можете перейти в защищенный режим. Или все было наоборот; вы можете получить 32-битный защищенный режим от EMM386 и принудительно отключить его.

Joshua 03.05.2024 22:46

@Joshua Интерфейс EMM для получения контроля над системой защищенного режима — VCPI. Однако хосты DPMI, такие как дозему и MSWindows, не предоставляют VCPI.

ecm 03.05.2024 23:51

Я использую nasm-2.16.03, и когда я выполняю «nasm -f bin Unreal.asm -o Unreal.obj», я получаю следующее сообщение об ошибке: Unreal.asm:52: ошибка: нераспознанный специальный символ '..start'

Fract 04.05.2024 00:19

@Fract: в комментариях была ошибка. Измените -f bin на -f obj. Затем вам нужно запустить компоновщик для создания EXE-файла. Я обновил ответ с исправлением.

Michael Petch 04.05.2024 03:47

Другие вопросы по теме