%if 0 Example of using MMX in DPMI protected mode Copyright (C) 1995-2003 Paul Vojta Copyright (C) 2008-2023 C. Masloch %endif %include "lmacros3.mac" numdef 32BITCODE, 0 numdef DUMPA32, 0 numdef DUMPOFFSET, 1 numdef DUMPES, 1 cpu 386 org 100h bits 16 start: pop ax ; get word saved on stack for COM files mov bx, sp add bx, 15 shr bx, 4 jnz .smallstack mov bx, 1000h ; it was a full 64 KiB stack .smallstack: mov ah, 4Ah ; free unused memory int 21h xor ax, ax xchg ax, word [2Ch] mov es, ax mov ah, 49h int 21h ; free environment if any mov ax, 1687h int 2Fh test ax, ax ; DPMI host installed? jnz nohost push es ; save DPMI entry address push di test si, si ; host requires client-specific DOS memory? jz .nomemneeded ; no --> mov bx, si mov ah, 48h int 21h ; allocate memory jc nomemory mov es, ax .nomemneeded: mov si, msg.debuginfo call printstring int3 mov bp, sp mov ax, 0001h ; start a 32-bit client call far [bp] ; initial switch to protected-mode jnc initsuccessful initfailed: mov si, msg.initfailed jmp short rmerror nohost: mov si, msg.nohost jmp short rmerror nomemory: mov si, msg.nomemory rmerror: call printstring mov ax, 4CFFh int 21h initsuccessful: initsuccessful_ofs equ (initsuccessful-$$+100h) ; now in protected mode %if _32BITCODE mov bx, cs mov cx, cs lar cx, cx shr cx, 8 or ch, 40h ; make a 32-bit cs mov ax, 9 int 31h ; now in 32-bit PM bits 32 now32bit: now32bit_ofs equ (now32bit-$$+100h) %endif mov word [ data.pspsel ], es push ds pop es %if _32BITCODE mov esi, msg.welcome call printstring %endif ; If we're in 32-bit PM then CPU is at least a 386. detect_mmx: ; Copied from init.asm, debugging messages ; and machine variable access removed. mov bx, sp ; save current stack pointer to align and sp, ~3 ; align stack to avoid AC fault pushfd ; push original EFLAGS pop eax ; get original EFLAGS mov ecx, eax ; save original EFLAGS in ECX (including IF) xor eax, 40000h ; flip AC bit in EFLAGS and ax, ~0200h ; clear IF push eax ; put new EFLAGS value on stack popfd ; replace EFLAGS value; DI pushfd ; get new EFLAGS pop eax ; store new EFLAGS value in EAX mov ax, cx ; ignore low bits (including IF) cmp eax, ecx je .cpudone_stack_eax_equals_ecx ; if 80386 --> ; Intel486 DX CPU, Intel487 SX NDP, and Intel486 SX CPU check. ; Checking for ability to set/clear ID flag (bit 21) in EFLAGS ; which indicates the presence of a processor with the ability ; to use the CPUID instruction. mov eax, ecx ; get original EFLAGS xor eax, 200000h ; flip ID bit in EFLAGS and ax, ~0200h ; clear IF push eax ; save new EFLAGS value on stack popfd ; replace current EFLAGS value; DI pushfd ; get new EFLAGS pop eax ; store new EFLAGS in EAX mov ax, cx ; ignore low bits (including IF) .cpudone_stack_eax_equals_ecx: push ecx popfd ; restore AC,ID bits and IF in EFLAGS (86 Mode) mov sp, bx ; restore sp cmp eax, ecx ; check if it's changed je .cpudone ; if it's a 486 (can't toggle ID bit) --> ; Execute CPUID instruction. subcpu 486 ; NASM (at least 2.10rc1) handles cpuid itself as a ; 586+ instruction, but we know better. So this ; part is declared for 486 compatibility, and only ; the cpuid instructions are emitted with 586 ; compatibility to appease NASM. xor eax, eax ; set up input for CPUID instruction [cpu 586] cpuid __CPU__ cmp eax, byte 1 jb .cpudone ; if 1 is not a valid input value for CPUID xor eax, eax ; otherwise, run CPUID with eax = 1 inc eax [cpu 586] cpuid __CPU__ test edx, 80_0000h jnz @F .cpudone: subcpureset mov esi, msg.nommx call printstring mov ax, 4CFFh int 21h @@: cpu 586 havemmx: havemmx_ofs equ (havemmx-$$+100h) finit movq mm0, qword [testvalue] xor eax, eax push eax push eax movzx ebx, sp movq qword [ebx], mm0 mov ebx, dump1 %if _DUMPES %define ESPREFIX es: %else %define ESPREFIX %endif %if _DUMPA32 %if _DUMPOFFSET o32 fnsave [dword ESPREFIX dump1] o32 fldenv [dword ESPREFIX dump1] %else o32 fnsave [ESPREFIX ebx] o32 fldenv [ESPREFIX ebx] %endif %else %if _DUMPOFFSET o32 fnsave [word ESPREFIX dump1] o32 fldenv [word ESPREFIX dump1] %else o32 fnsave [ESPREFIX bx] o32 fldenv [ESPREFIX bx] %endif %endif call disp_dump mov ebx, dump2 %if _DUMPA32 %if _DUMPOFFSET o32 fnsave [dword ESPREFIX dump2] o32 fldenv [dword ESPREFIX dump2] %else o32 fnsave [ESPREFIX ebx] o32 fldenv [ESPREFIX ebx] %endif %else %if _DUMPOFFSET o32 fnsave [word ESPREFIX dump2] o32 fldenv [word ESPREFIX dump2] %else o32 fnsave [ESPREFIX bx] o32 fldenv [ESPREFIX bx] %endif %endif call disp_dump mov esi, msg.firstread call printstring pop eax pop edx call disp_edxeax_hex or edx, eax push edx mov esi, msg.linebreak call printstring pop edx test edx, edx jnz @F mov esi, msg.firstzero call printstring mov ax, 4CFFh int 21h @@: xor eax, eax push eax push eax movzx ebx, sp movq qword [ebx], mm0 mov esi, msg.secondread call printstring pop eax pop edx call disp_edxeax_hex or edx, eax push edx mov esi, msg.linebreak call printstring pop edx test edx, edx jnz @F mov esi, msg.secondzero call printstring mov ax, 4CFFh int 21h @@: mov esi, msg.mmxgood call printstring mov ax, 4C00h int 21h ; normal client exit (terminates DOS process too) ; Print a string with simple instructions. Don't use ; pointers or instructions depending on the default operation ; size, this is called in both 16- and 32-bit modes. printstring.next: mov dl, al mov ah, 2 int 21h printstring: lodsb test al, al jnz .next retn disp_dump: xor edx, edx mov ecx, 128 / 16 .loop_line: xchg eax, edx call disp_ax_hex xchg eax, edx mov al, 32 call disp_al call disp_al push ecx mov cl, 16 .loop_byte: mov al, byte [ebx + edx] call disp_al_hex mov al, 32 call disp_al inc edx loop .loop_byte pop ecx mov al, 13 call disp_al mov al, 10 call disp_al loop .loop_line mov al, 13 call disp_al mov al, 10 call disp_al retn disp_edxeax_hex: xchg eax, edx call disp_eax_hex xchg eax, edx disp_eax_hex: rol eax, 16 call disp_ax_hex rol eax, 16 disp_ax_hex: xchg al, ah call disp_al_hex xchg al, ah disp_al_hex: rol al, 4 call disp_nybble_hex rol al, 4 disp_nybble_hex: push ax and al, 0Fh cmp al, 10 jb .decit add al, 'A' - ('0' + 10) .decit: add al, '0' call disp_al pop ax retn disp_al: push ax push dx xchg dx, ax mov ah, 02h int 21h pop dx pop ax retn align 8, db 0 dump1: times 128 db 0 dump2: times 128 db 0CCh align 8, db 0 testvalue: dq 1138_0000_2642h align 2, db 0 data: .pspsel: dw 0 msg: .nohost: asciz "No DPMI host installed.",13,10 .nomemory: asciz "Not enough DOS memory for client initialization.",13,10 .initfailed: asciz "DPMI initialization failed.",13,10 .debuginfo: db "Protected mode breakpoint at ",_4digitshex(initsuccessful_ofs),"h.",13,10 %if _32BITCODE db "32-bit code segment breakpoint at ",_4digitshex(now32bit_ofs),"h.",13,10 %endif db "Have MMX breakpoint at ",_4digitshex(havemmx_ofs),"h.",13,10 asciz 13,10 .welcome: asciz "Welcome in 32-bit protected mode.",13,10 .nommx: asciz "Error: MMX not supported.",13,10 .firstread: asciz "First read returned",9 .secondread: asciz "Second read returned",9 .firstzero: asciz "Error: MMX read returns zero on first read.",13,10 .secondzero: asciz "Error: MMX read returns zero on second read.",13,10 .mmxgood: asciz "MMX single-register R/W seems to work.",13,10 .linebreak: asciz 13,10