New upstream version 2.0pre9.2
This commit is contained in:
5
test/ecm/dpmitest/Makefile
Normal file
5
test/ecm/dpmitest/Makefile
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
all: dpmialoc.com dpmimini.com dpmims.com dpmipsp.com
|
||||
|
||||
%.com: %.asm
|
||||
nasm -f bin -I../lmacros/ -o $@ $<
|
||||
595
test/ecm/dpmitest/dpmialoc.asm
Normal file
595
test/ecm/dpmitest/dpmialoc.asm
Normal file
@@ -0,0 +1,595 @@
|
||||
|
||||
%if 0
|
||||
|
||||
Example of an application running in DPMI-allocated memory
|
||||
Public Domain
|
||||
|
||||
%endif
|
||||
|
||||
%include "lmacros3.mac"
|
||||
|
||||
|
||||
cpu 386
|
||||
org 100h
|
||||
addsection R86M_MEM_SECTION, start=100h
|
||||
addsection DPMI_MEM_SECTION, vstart=0 follows=R86M_MEM_SECTION align=16
|
||||
|
||||
usesection R86M_MEM_SECTION
|
||||
bits 16
|
||||
r86m_start:
|
||||
pop ax ; get word saved on stack for COM files
|
||||
mov bx, sp
|
||||
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 bx, 80h >> 4
|
||||
mov ah, 48h
|
||||
int 21h ; allocate memory for child process
|
||||
jc nomemory
|
||||
|
||||
; ax:0-> DOS memory allocated for child process
|
||||
mov es, ax
|
||||
xor di, di ; es:di->
|
||||
xor si, si ; cs:si = ds:si -> our PSP
|
||||
mov cx, 128 >> 1 ; PSP size
|
||||
rep movsw ; copy process segment prefix into newly allocated block
|
||||
dec ax
|
||||
mov ds, ax ; ds = MCB of allocated block
|
||||
inc ax ; ax = allocated block!
|
||||
mov word [ 8 ], "DP"
|
||||
mov word [ 8+2 ], "MI"
|
||||
mov word [ 8+4 ], "PS"
|
||||
mov word [ 8+6 ], "PC" ; Force MCB string to "DPMIPSPC"
|
||||
; mov word [ 1 ], ax ; Set owner to itself
|
||||
; (leave owner as us)
|
||||
|
||||
mov es, ax ; es = new PSP
|
||||
mov dx, cs
|
||||
mov ds, dx ; ds = old PSP
|
||||
mov di, 18h
|
||||
mov cx, 20
|
||||
mov word [ es:32h ], cx
|
||||
mov word [ es:34h ], di
|
||||
mov word [ es:34h+2 ], ax ; fix the new PSP's PHT pointer
|
||||
mov word [ es:0Ah ], childterminated
|
||||
mov word [ es:0Ah+2 ], dx ; set termination address
|
||||
mov word [ es:16h ], dx ; set parent PSP to current one
|
||||
|
||||
; The stack address set on the child's termination
|
||||
; is that of the last Int21 call to a usual function
|
||||
; (such as Int21.48 or .45) from the parent process.
|
||||
; All registers will be lost though, because we don't
|
||||
; build a fake stack frame.
|
||||
|
||||
push di
|
||||
mov al, -1
|
||||
rep stosb ; initialize new PHT with empty entries
|
||||
pop di
|
||||
mov cx, word [ 32h ] ; = number of current process handles
|
||||
jcxz .phtdone ; (weird, but handle this correctly)
|
||||
cmp cx, 20
|
||||
jbe .normalpht
|
||||
mov cx, 20
|
||||
.normalpht:
|
||||
lds si, [ 34h ] ; -> current process handle table
|
||||
xor bx, bx
|
||||
.phtloop:
|
||||
mov dl, -1
|
||||
cmp byte [si+bx], dl ; source PHT handle used?
|
||||
je .phtnext ; no -->
|
||||
mov ah, 45h
|
||||
int 21h ; DUP
|
||||
jc nohandle
|
||||
xchg ax, bx
|
||||
xchg dl, byte [si+bx] ; get DUPed handle and kill PHT entry
|
||||
xchg ax, bx
|
||||
mov byte [es:di+bx], dl ; write into PHT of new process
|
||||
.phtnext:
|
||||
inc bx ; next handle
|
||||
loop .phtloop
|
||||
.phtdone:
|
||||
push cs
|
||||
pop ds
|
||||
|
||||
mov bx, es
|
||||
mov ah, 50h
|
||||
int 21h ; set current PSP to new
|
||||
; mov ah, 1Ah
|
||||
; mov ds,
|
||||
; mov dx, 80h
|
||||
; int 21h ; set DTA
|
||||
|
||||
; Now executing in the child process's environment.
|
||||
; Terminating will return to label childterminated.
|
||||
|
||||
mov ax, cs
|
||||
mov word [rmcallstruc0.cs], ax
|
||||
mov word [rmcallstruc0.ds], ax
|
||||
mov word [rmcallstruc0.es], ax
|
||||
mov word [rmcallstruc0.ss], ax
|
||||
pushf
|
||||
pop word [rmcallstruc0.flags] ; setup with RM values
|
||||
|
||||
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 ; alloc DOS memory
|
||||
int 21h
|
||||
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
|
||||
nohandle:
|
||||
cmp cx, 20
|
||||
je .no_handles_duplicated_yet
|
||||
; In this case, there may be some already duplicated
|
||||
; file handles. Switch to the child process and then
|
||||
; terminate it, in order to close the handles.
|
||||
mov word [ es:0Ah ], .half_initialised_child_terminated
|
||||
; set Parent Return Address to ours
|
||||
mov bx, es
|
||||
mov ah, 50h
|
||||
int 21h
|
||||
mov ax, 4C00h
|
||||
int 21h
|
||||
|
||||
.half_initialised_child_terminated:
|
||||
cld
|
||||
.no_handles_duplicated_yet:
|
||||
push cs
|
||||
pop ds
|
||||
mov si, msg.nohandle
|
||||
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
|
||||
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)
|
||||
mov word [ data.pspsel ], es
|
||||
push ds
|
||||
pop es
|
||||
|
||||
mov esi, msg.welcome
|
||||
call printstring
|
||||
|
||||
mov [r86m_end + dpmi_to_r86m_code_sel - dpmi_start], cs
|
||||
mov [r86m_end + dpmi_to_r86m_data_sel - dpmi_start], ds
|
||||
|
||||
xor eax, eax
|
||||
mov cx, 2
|
||||
int 31h ; allocate 2 selectors
|
||||
jc @F
|
||||
xchg eax, ebx
|
||||
|
||||
mov eax, 3
|
||||
int 31h
|
||||
jc @F
|
||||
|
||||
mov dword [r86m_end + dpmi_dpmi_code_sel - dpmi_start], ebx
|
||||
push ebx
|
||||
add ebx, eax
|
||||
mov dword [r86m_end + dpmi_dpmi_data_sel - dpmi_start], ebx
|
||||
mov ax, 8
|
||||
or ecx, -1
|
||||
mov edx, ecx
|
||||
int 31h ; set limit to -1
|
||||
pop ebx
|
||||
jc @F
|
||||
|
||||
mov ax, 8
|
||||
int 31h ; set limit to -1
|
||||
jc @F
|
||||
|
||||
mov ax, 9
|
||||
mov cx, 1100_1111_1_11_1_1011b
|
||||
int 31h ; set to 32-bit readable code segment
|
||||
jc @F
|
||||
|
||||
mov ax, 0501h
|
||||
mov bx, (dpmi_end - dpmi_start) >> 16
|
||||
mov cx, (dpmi_end - dpmi_start) & 0FFFFh
|
||||
int 31h
|
||||
jc @F
|
||||
|
||||
push bx
|
||||
push cx
|
||||
pop dword [data.dpmi_block_address]
|
||||
push si
|
||||
push di
|
||||
pop dword [data.dpmi_block_handle]
|
||||
|
||||
mov ebx, dword [r86m_end + dpmi_dpmi_data_sel - dpmi_start]
|
||||
mov edx, dword [data.dpmi_block_address]
|
||||
mov ecx, dword [data.dpmi_block_address + 2]
|
||||
mov ax, 7
|
||||
int 31h
|
||||
jc @F
|
||||
|
||||
push ecx
|
||||
mov es, ebx
|
||||
xor edi, edi
|
||||
mov esi, r86m_end
|
||||
mov ecx, dwords(dpmi_end - dpmi_start)
|
||||
rep movsd
|
||||
pop ecx
|
||||
|
||||
mov ebx, dword [r86m_end + dpmi_dpmi_code_sel - dpmi_start]
|
||||
mov ax, 7
|
||||
int 31h
|
||||
jc @F
|
||||
|
||||
mov esi, msg.info2.1
|
||||
call printstring
|
||||
mov eax, dword [data.dpmi_block_address]
|
||||
call disp_eax_hex
|
||||
mov esi, msg.info2.2
|
||||
call printstring
|
||||
mov eax, ebx
|
||||
call disp_ax_hex
|
||||
mov esi, msg.info2.3
|
||||
call printstring
|
||||
mov eax, ebx
|
||||
call disp_ax_hex
|
||||
mov esi, msg.info2.4
|
||||
call printstring
|
||||
mov eax, ebx
|
||||
call disp_ax_hex
|
||||
mov esi, msg.info2.5
|
||||
call printstring
|
||||
|
||||
int3
|
||||
push ebx
|
||||
push dpmi_start
|
||||
retf
|
||||
|
||||
@@:
|
||||
push ss
|
||||
pop ds
|
||||
mov esi, msg.error
|
||||
call printstring
|
||||
or eax, -1
|
||||
|
||||
pmend:
|
||||
push ss
|
||||
pop ds
|
||||
push eax
|
||||
mov ebx, dword [r86m_end + dpmi_dpmi_code_sel - dpmi_start]
|
||||
mov ax, 1
|
||||
int 31h
|
||||
mov ebx, dword [r86m_end + dpmi_dpmi_data_sel - dpmi_start]
|
||||
mov ax, 1
|
||||
int 31h
|
||||
mov edi, dword [data.dpmi_block_handle]
|
||||
mov esi, dword [data.dpmi_block_handle + 2]
|
||||
cmp edi, -1
|
||||
je @F
|
||||
mov ax, 0502h
|
||||
int 31h
|
||||
@@:
|
||||
pop eax
|
||||
mov ah, 4Ch ; normal client exit
|
||||
int 21h
|
||||
|
||||
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_al_nybble_hex
|
||||
rol al, 4
|
||||
disp_al_nybble_hex:
|
||||
push eax
|
||||
and al, 0Fh
|
||||
add al, '0'
|
||||
cmp al, '9'
|
||||
jbe @F
|
||||
add al, -'9' -1 +'A'
|
||||
@@:
|
||||
call disp_al
|
||||
pop eax
|
||||
retn
|
||||
|
||||
disp_al:
|
||||
push edx
|
||||
push eax
|
||||
mov dl, al
|
||||
mov ah, 2
|
||||
int 21h
|
||||
pop eax
|
||||
pop edx
|
||||
retn
|
||||
|
||||
|
||||
bits 16
|
||||
|
||||
callrm:
|
||||
; int3 ; int 03h or int3 traps (in real or V86 mode) don't cause any problems.
|
||||
int strict byte 03h
|
||||
xor ax, ax
|
||||
push ax
|
||||
push ax
|
||||
call -1:-1
|
||||
.callback equ $-4
|
||||
pop ax
|
||||
pop ax
|
||||
; int3
|
||||
int strict byte 03h
|
||||
iret
|
||||
|
||||
|
||||
; 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
|
||||
|
||||
|
||||
; The (DPMIed) child process has terminated.
|
||||
childterminated:
|
||||
childterminated_ofs equ (childterminated-$$+100h)
|
||||
cld
|
||||
push cs
|
||||
pop ds
|
||||
mov ah, 4Dh
|
||||
int 21h
|
||||
mov si, msg.backsuccess
|
||||
test al, al
|
||||
jz .success
|
||||
mov si, msg.backerror
|
||||
.success:
|
||||
call printstring
|
||||
mov ax, 4C00h
|
||||
int 21h ; terminate DOS process
|
||||
|
||||
|
||||
align 4
|
||||
data:
|
||||
.dpmi_block_address: dd 0
|
||||
.dpmi_block_handle: dd -1
|
||||
.pspsel: dw 0
|
||||
|
||||
align 4
|
||||
; This one is used to call down to our RM part
|
||||
; from the 32-bit PM code. All values are filled
|
||||
; in either here or in our RM initialization.
|
||||
rmcallstruc0:
|
||||
.edi: dd 0
|
||||
.esi: dd 0
|
||||
.ebp: dd 0
|
||||
dd 0
|
||||
.ebx: dd 0
|
||||
.edx: dd 0
|
||||
.ecx: dd 0
|
||||
.eax: dd 0
|
||||
.flags: dw 0
|
||||
.es: dw 0
|
||||
.ds: dw 0
|
||||
.fs: dw 0
|
||||
.gs: dw 0
|
||||
.ip: dw callrm
|
||||
.cs: dw 0
|
||||
.sp: dw rmcallsp
|
||||
.ss: dw 0
|
||||
|
||||
align 4
|
||||
; This one is utilized by the DPMI host and
|
||||
; our RM callback when called from RM. All
|
||||
; values are filled in by the DPMI host.
|
||||
rmcallstruc1:
|
||||
.edi: dd 0
|
||||
.esi: dd 0
|
||||
.ebp: dd 0
|
||||
dd 0
|
||||
.ebx: dd 0
|
||||
.edx: dd 0
|
||||
.ecx: dd 0
|
||||
.eax: dd 0
|
||||
.flags: dw 0
|
||||
.es: dw 0
|
||||
.ds: dw 0
|
||||
.fs: dw 0
|
||||
.gs: dw 0
|
||||
.ip: dw 0
|
||||
.cs: dw 0
|
||||
.sp: dw 0
|
||||
.ss: dw 0
|
||||
|
||||
msg:
|
||||
.backsuccess: asciz "Child process terminated okay, back in real mode.",13,10
|
||||
.backerror: asciz "Child process terminated with error, back in real mode.",13,10
|
||||
.error: asciz "Error during allocation setup.",13,10
|
||||
.info2.1: asciz "DPMI allocation at "
|
||||
.info2.2: asciz "h.",13,10,"DPMI allocation entrypoint at "
|
||||
.info2.3: db "h:00000000h.",13,10
|
||||
asciz "Real mode procedure called at "
|
||||
.info2.4: db "h:",_8digitshex(dpmi_start.callingrm - dpmi_start),"h.",13,10
|
||||
asciz "DPMI allocation exit at "
|
||||
.info2.5: asciz "h:",_8digitshex(dpmi_start.return - dpmi_start),"h.",13,10
|
||||
|
||||
.nohost: asciz "No DPMI host installed.",13,10
|
||||
.nohandle: asciz "No free DOS file handle for child process creation.",13,10
|
||||
.nomemory: asciz "Not enough DOS memory for child process creation or client initialization.",13,10
|
||||
.initfailed: asciz "DPMI initialization failed.",13,10
|
||||
.debuginfo: db "Protected mode breakpoint at ",_4digitshex(initsuccessful_ofs),"h.",13,10
|
||||
db "32-bit code segment breakpoint at ",_4digitshex(now32bit_ofs),"h.",13,10
|
||||
db "Return from child process at ",_4digitshex(childterminated_ofs),"h.",13,10
|
||||
asciz 13,10
|
||||
.welcome: asciz "Welcome in 32-bit protected mode.",13,10
|
||||
|
||||
align 16
|
||||
times 256 db 26h
|
||||
align 2
|
||||
rmcallsp:
|
||||
|
||||
align 16
|
||||
r86m_end:
|
||||
usesection DPMI_MEM_SECTION
|
||||
bits 32
|
||||
dpmi_start:
|
||||
nop
|
||||
mov ds, [cs:dpmi_dpmi_data_sel]
|
||||
mov esi, dpmi_msg.hello
|
||||
call dpmi_printstring
|
||||
|
||||
mov ax, 0303h
|
||||
push cs
|
||||
pop ds
|
||||
mov esi, dpmi_callback ; ds:esi-> called procedure
|
||||
push ss
|
||||
pop es
|
||||
mov edi, rmcallstruc1 ; es:edi-> (inreentrant) real mode call structure
|
||||
int 31h ; allocate RM callback
|
||||
push ss
|
||||
pop ds
|
||||
jc .nocallback
|
||||
mov dword [callrm.callback], edx
|
||||
mov word [callrm.callback+2], cx
|
||||
|
||||
mov ax, 0302h
|
||||
xor ebx, ebx
|
||||
xor ecx, ecx
|
||||
mov edi, rmcallstruc0
|
||||
.callingrm:
|
||||
int 31h ; call RM procedure with interrupt stack
|
||||
nop
|
||||
jc .callrmfailed
|
||||
|
||||
mov ecx, dword [callrm.callback+2]
|
||||
mov edx, dword [callrm.callback]
|
||||
mov ax, 0304h
|
||||
int 31h ; free RM callback
|
||||
mov ds, [cs:dpmi_dpmi_data_sel]
|
||||
mov esi, dpmi_msg.bye
|
||||
call dpmi_printstring
|
||||
.return:
|
||||
nop
|
||||
xor eax, eax
|
||||
.ret_al:
|
||||
jmp far [cs:dpmi_return_address]
|
||||
|
||||
|
||||
.callrmfailed:
|
||||
mov esi, dpmi_msg.callrmfailed
|
||||
jmp short @F
|
||||
.nocallback:
|
||||
mov esi, dpmi_msg.nocallback
|
||||
@@:
|
||||
push cs
|
||||
pop ds
|
||||
call dpmi_printstring
|
||||
push ss
|
||||
pop ds
|
||||
movzx ecx, word [callrm.callback+2]
|
||||
movzx edx, word [callrm.callback]
|
||||
mov eax, edx
|
||||
and eax, ecx
|
||||
inc ax
|
||||
jnz @F
|
||||
mov ax, 0304h
|
||||
int 31h ; free RM callback
|
||||
@@:
|
||||
or eax, -1
|
||||
jmp .ret_al
|
||||
|
||||
|
||||
; This is a RM callback. Called from RM, executed in PM.
|
||||
;
|
||||
; INP: es:edi-> RM call structure
|
||||
; ds:esi-> RM stack
|
||||
; ss:esp-> DPMI host internal stack
|
||||
; CHG: all (?) except es:edi
|
||||
; read/write RM call structure to communicate with RM code
|
||||
dpmi_callback:
|
||||
push dword [esi]
|
||||
pop dword [es:edi+2Ah] ; set RM cs:ip
|
||||
add word [es:edi+2Eh], byte 4 ; pop 4 byte from the stack
|
||||
int strict byte 03h
|
||||
nop
|
||||
int3
|
||||
nop
|
||||
iret
|
||||
|
||||
|
||||
; Print a string with simple instructions.
|
||||
dpmi_printstring.next:
|
||||
mov dl, al
|
||||
mov ah, 2
|
||||
int 21h
|
||||
dpmi_printstring:
|
||||
lodsb
|
||||
test al, al
|
||||
jnz .next
|
||||
retn
|
||||
|
||||
|
||||
dpmi_msg:
|
||||
.hello: asciz "Hello from DPMI memory section!",13,10
|
||||
.nocallback: asciz "Could not allocate real mode callback.",13,10
|
||||
.callrmfailed: asciz "Calling real mode procedure failed.",13,10
|
||||
.bye: asciz "Calling real mode procedure which called callback successful.",13,10
|
||||
|
||||
|
||||
align 4
|
||||
dpmi_return_address: dd pmend
|
||||
dpmi_to_r86m_code_sel: dd 0
|
||||
dpmi_to_r86m_data_sel: dd 0
|
||||
dpmi_dpmi_code_sel: dd 0
|
||||
dpmi_dpmi_data_sel: dd 0
|
||||
|
||||
align 16
|
||||
dpmi_end:
|
||||
125
test/ecm/dpmitest/dpmimini.asm
Normal file
125
test/ecm/dpmitest/dpmimini.asm
Normal file
@@ -0,0 +1,125 @@
|
||||
|
||||
%if 0
|
||||
|
||||
Example of entering 32-bit protected mode using DPMI
|
||||
Public Domain
|
||||
|
||||
%endif
|
||||
|
||||
;%include "CMMACROS.MAC"
|
||||
%define _4digitshex(h) ((((h)/1000h)% 10h)+'0' +(('A'-'9'-1)*((((h)/1000h)% 10h)/0Ah))), \
|
||||
((((h)/100h)% 10h)+'0' +(('A'-'9'-1)*((((h)/100h)% 10h)/0Ah))), \
|
||||
((((h)/10h)% 10h)+'0' +(('A'-'9'-1)*((((h)/10h)% 10h)/0Ah))), \
|
||||
(((h)% 10h)+'0' +(('A'-'9'-1)*(((h)% 10h)/0Ah)))
|
||||
; ASCIZ string
|
||||
;
|
||||
; %1+ = Optional string
|
||||
%imacro asciz 0-1+.nolist
|
||||
%if %0 >= 1
|
||||
db %1
|
||||
%endif
|
||||
db 0
|
||||
%endmacro
|
||||
|
||||
|
||||
cpu 386
|
||||
org 100h
|
||||
bits 16
|
||||
start:
|
||||
pop ax ; get word saved on stack for COM files
|
||||
mov bx, sp
|
||||
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
|
||||
or ax, ax ; DPMI host installed?
|
||||
jnz nohost
|
||||
push es ; save DPMI entry address
|
||||
push di
|
||||
or 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
|
||||
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)
|
||||
mov word [ data.pspsel ], es
|
||||
push ds
|
||||
pop es
|
||||
|
||||
mov esi, msg.welcome
|
||||
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
|
||||
or al, al
|
||||
jnz .next
|
||||
retn
|
||||
|
||||
align 2
|
||||
data:
|
||||
.pspsel: dw 0
|
||||
|
||||
msg:
|
||||
.nohost: asciz "No DPMI host installed.",13,10
|
||||
.nomemory: asciz "Not enough DOS memory for child process creation or client initialization.",13,10
|
||||
.initfailed: asciz "DPMI initialization failed.",13,10
|
||||
.debuginfo: db "Protected mode breakpoint at ",_4digitshex(initsuccessful_ofs),"h.",13,10
|
||||
db "32-bit code segment breakpoint at ",_4digitshex(now32bit_ofs),"h.",13,10
|
||||
asciz 13,10
|
||||
.welcome: asciz "Welcome in 32-bit protected mode.",13,10
|
||||
235
test/ecm/dpmitest/dpmims.asm
Normal file
235
test/ecm/dpmitest/dpmims.asm
Normal file
@@ -0,0 +1,235 @@
|
||||
|
||||
%if 0
|
||||
|
||||
Example of using DPMI raw-mode switching
|
||||
Public Domain
|
||||
|
||||
%endif
|
||||
|
||||
;%include "CMMACROS.MAC"
|
||||
%define _4digitshex(h) ((((h)/1000h)% 10h)+'0' +(('A'-'9'-1)*((((h)/1000h)% 10h)/0Ah))), \
|
||||
((((h)/100h)% 10h)+'0' +(('A'-'9'-1)*((((h)/100h)% 10h)/0Ah))), \
|
||||
((((h)/10h)% 10h)+'0' +(('A'-'9'-1)*((((h)/10h)% 10h)/0Ah))), \
|
||||
(((h)% 10h)+'0' +(('A'-'9'-1)*(((h)% 10h)/0Ah)))
|
||||
; ASCIZ string
|
||||
;
|
||||
; %1+ = Optional string
|
||||
%imacro asciz 0-1+.nolist
|
||||
%if %0 >= 1
|
||||
db %1
|
||||
%endif
|
||||
db 0
|
||||
%endmacro
|
||||
|
||||
|
||||
%assign _AUXBUFFSIZE 8192
|
||||
|
||||
cpu 386
|
||||
org 100h
|
||||
bits 16
|
||||
start:
|
||||
pop ax ; get word saved on stack for COM files
|
||||
mov bx, sp
|
||||
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 word [pspdbg], cs
|
||||
|
||||
mov ax, 1687h
|
||||
int 2Fh
|
||||
or ax, ax ; DPMI host installed?
|
||||
jnz nohost
|
||||
push es ; save DPMI entry address
|
||||
push di
|
||||
or 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
|
||||
mov word [ data.pspsel ], es
|
||||
push ds
|
||||
pop es
|
||||
|
||||
mov si, msg.welcome
|
||||
call printstring
|
||||
|
||||
mov word [code_sel], cs
|
||||
mov word [dssel], ds
|
||||
|
||||
xor edi, edi
|
||||
mov ax, 0305h ; get raw mode-switch save state addresses
|
||||
int 31h
|
||||
jc .cannotswitch
|
||||
cmp ax, _AUXBUFFSIZE ; fits into auxbuff ?
|
||||
ja .cannotswitch ; no -->
|
||||
test ax, ax
|
||||
jz .nobuffer
|
||||
mov word [dpmi_rmsav+0], cx
|
||||
mov word [dpmi_rmsav+2], bx
|
||||
mov dword [dpmi_pmsav], edi
|
||||
mov word [dpmi_pmsav+4], si
|
||||
jmp .gotbuffer
|
||||
|
||||
.nobuffer:
|
||||
mov word [dpmi_rmsav+0], ..@retf
|
||||
push word [pspdbg]
|
||||
pop word [dpmi_rmsav+2]
|
||||
mov dword [dpmi_pmsav+0], ..@retf
|
||||
mov word [dpmi_pmsav+4], cs
|
||||
|
||||
.gotbuffer:
|
||||
xor edi, edi ; clear edih
|
||||
mov ax, 0306h ; get raw mode-switch addresses
|
||||
int 31h
|
||||
jc .cannotswitch
|
||||
mov word [dpmi_rm2pm+0], cx
|
||||
mov word [dpmi_rm2pm+2], bx
|
||||
mov dword [dpmi_pm2rm], edi
|
||||
mov word [dpmi_pm2rm+4], si
|
||||
|
||||
mov al, 0
|
||||
call sr_state_pm
|
||||
call switchmode_pm2rm
|
||||
|
||||
mov si, msg.rm
|
||||
call printstring
|
||||
|
||||
call switchmode_rm2pm
|
||||
mov al, 1
|
||||
call sr_state_pm
|
||||
|
||||
mov si, msg.pmagain
|
||||
call printstring
|
||||
|
||||
.cannotswitch:
|
||||
mov ax, 4C00h
|
||||
int 21h ; normal client exit (terminates DOS process too)
|
||||
|
||||
..@retf:
|
||||
retf
|
||||
|
||||
|
||||
switchmode_pm2rm:
|
||||
;--- raw switch:
|
||||
;--- si:e/di: new cs:e/ip
|
||||
;--- dx:e/bx: new ss:e/sp
|
||||
;--- ax: new ds
|
||||
;--- cx: new es
|
||||
xor ebx, ebx ; clear ebxh
|
||||
mov bx, sp
|
||||
xor edi, edi ; clear edih
|
||||
mov di, .back_after_switch
|
||||
|
||||
mov ax, [pspdbg] ; switch pm -> rm
|
||||
mov si, ax
|
||||
mov dx, ax
|
||||
mov cx, ax
|
||||
jmp far dword [dpmi_pm2rm]
|
||||
|
||||
.back_after_switch:
|
||||
retn
|
||||
|
||||
|
||||
switchmode_rm2pm:
|
||||
xor ebx, ebx ; clear ebxh
|
||||
mov bx, sp
|
||||
xor edi, edi ; clear edih
|
||||
mov di, .back_after_switch
|
||||
|
||||
mov ax, [dssel] ; switch rm -> pm
|
||||
mov si, [code_sel]
|
||||
mov dx, ax
|
||||
mov cx, ax
|
||||
jmp far [dpmi_rm2pm]
|
||||
|
||||
.back_after_switch:
|
||||
retn
|
||||
|
||||
|
||||
sr_state_rm:
|
||||
mov edi, auxbuff
|
||||
push ds
|
||||
pop es
|
||||
call far [dpmi_rmsav]
|
||||
retn
|
||||
|
||||
|
||||
sr_state_pm:
|
||||
mov edi, auxbuff
|
||||
push ds
|
||||
pop es
|
||||
call far dword [dpmi_pmsav]
|
||||
retn
|
||||
|
||||
|
||||
; 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
|
||||
or al, al
|
||||
jnz .next
|
||||
retn
|
||||
|
||||
align 4
|
||||
data:
|
||||
.pspsel: dw 0
|
||||
dssel: dw 0
|
||||
code_sel: dw 0
|
||||
pspdbg: dw 0
|
||||
dpmi_rm2pm: dd 0
|
||||
dpmi_rmsav: dd 0
|
||||
dpmi_pm2rm: times 3 dw 0
|
||||
dpmi_pmsav: times 3 dw 0
|
||||
|
||||
align 16
|
||||
auxbuff: times _AUXBUFFSIZE db 0
|
||||
|
||||
msg:
|
||||
.nohost: asciz "No DPMI host installed.",13,10
|
||||
.nomemory: asciz "Not enough DOS memory for child process creation or client initialization.",13,10
|
||||
.initfailed: asciz "DPMI initialization failed.",13,10
|
||||
.debuginfo: db "Protected mode breakpoint at ",_4digitshex(initsuccessful_ofs),"h.",13,10
|
||||
asciz 13,10
|
||||
.welcome: asciz "Welcome in protected mode.",13,10
|
||||
.rm: asciz "Mode-switched to real mode.",13,10
|
||||
.pmagain: asciz "In protected mode again.",13,10
|
||||
393
test/ecm/dpmitest/dpmipsp.asm
Normal file
393
test/ecm/dpmitest/dpmipsp.asm
Normal file
@@ -0,0 +1,393 @@
|
||||
|
||||
%if 0
|
||||
|
||||
Example of a DPMI fake child process returning
|
||||
Public Domain
|
||||
|
||||
%endif
|
||||
|
||||
;%include "CMMACROS.MAC"
|
||||
%define _4digitshex(h) ((((h)/1000h)% 10h)+'0' +(('A'-'9'-1)*((((h)/1000h)% 10h)/0Ah))), \
|
||||
((((h)/100h)% 10h)+'0' +(('A'-'9'-1)*((((h)/100h)% 10h)/0Ah))), \
|
||||
((((h)/10h)% 10h)+'0' +(('A'-'9'-1)*((((h)/10h)% 10h)/0Ah))), \
|
||||
(((h)% 10h)+'0' +(('A'-'9'-1)*(((h)% 10h)/0Ah)))
|
||||
; ASCIZ string
|
||||
;
|
||||
; %1+ = Optional string
|
||||
%imacro asciz 0-1+.nolist
|
||||
%if %0 >= 1
|
||||
db %1
|
||||
%endif
|
||||
db 0
|
||||
%endmacro
|
||||
|
||||
|
||||
cpu 386
|
||||
org 100h
|
||||
bits 16
|
||||
start:
|
||||
pop ax ; get word saved on stack for COM files
|
||||
mov bx, sp
|
||||
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 bx, 80h >> 4
|
||||
mov ah, 48h
|
||||
int 21h ; allocate memory for child process
|
||||
jc nomemory
|
||||
|
||||
; ax:0-> DOS memory allocated for child process
|
||||
mov es, ax
|
||||
xor di, di ; es:di->
|
||||
xor si, si ; cs:si = ds:si -> our PSP
|
||||
mov cx, 128 >> 1 ; PSP size
|
||||
rep movsw ; copy process segment prefix into newly allocated block
|
||||
dec ax
|
||||
mov ds, ax ; ds = MCB of allocated block
|
||||
inc ax ; ax = allocated block!
|
||||
mov word [ 8 ], "DP"
|
||||
mov word [ 8+2 ], "MI"
|
||||
mov word [ 8+4 ], "PS"
|
||||
mov word [ 8+6 ], "PC" ; Force MCB string to "DPMIPSPC"
|
||||
; mov word [ 1 ], ax ; Set owner to itself
|
||||
; (leave owner as us)
|
||||
|
||||
mov es, ax ; es = new PSP
|
||||
mov dx, cs
|
||||
mov ds, dx ; ds = old PSP
|
||||
mov di, 18h
|
||||
mov cx, 20
|
||||
mov word [ es:32h ], cx
|
||||
mov word [ es:34h ], di
|
||||
mov word [ es:34h+2 ], ax ; fix the new PSP's PHT pointer
|
||||
mov word [ es:0Ah ], childterminated
|
||||
mov word [ es:0Ah+2 ], dx ; set termination address
|
||||
mov word [ es:16h ], dx ; set parent PSP to current one
|
||||
|
||||
; The stack address set on the child's termination
|
||||
; is that of the last Int21 call to a usual function
|
||||
; (such as Int21.48 or .45) from the parent process.
|
||||
; All registers will be lost though, because we don't
|
||||
; build a fake stack frame.
|
||||
|
||||
push di
|
||||
mov al, -1
|
||||
rep stosb ; initialize new PHT with empty entries
|
||||
pop di
|
||||
mov cx, word [ 32h ] ; = number of current process handles
|
||||
jcxz .phtdone ; (weird, but handle this correctly)
|
||||
cmp cx, 20
|
||||
jbe .normalpht
|
||||
mov cx, 20
|
||||
.normalpht:
|
||||
lds si, [ 34h ] ; -> current process handle table
|
||||
xor bx, bx
|
||||
.phtloop:
|
||||
mov dl, -1
|
||||
cmp byte [si+bx], dl ; source PHT handle used?
|
||||
je .phtnext ; no -->
|
||||
mov ah, 45h
|
||||
int 21h ; DUP
|
||||
jc nohandle
|
||||
xchg ax, bx
|
||||
xchg dl, byte [si+bx] ; get DUPed handle and kill PHT entry
|
||||
xchg ax, bx
|
||||
mov byte [es:di+bx], dl ; write into PHT of new process
|
||||
.phtnext:
|
||||
inc bx ; next handle
|
||||
loop .phtloop
|
||||
.phtdone:
|
||||
push cs
|
||||
pop ds
|
||||
|
||||
mov bx, es
|
||||
mov ah, 50h
|
||||
int 21h ; set current PSP to new
|
||||
; mov ah, 1Ah
|
||||
; mov ds,
|
||||
; mov dx, 80h
|
||||
; int 21h ; set DTA
|
||||
|
||||
; Now executing in the child process's environment.
|
||||
; Terminating will return to label childterminated.
|
||||
|
||||
mov ax, cs
|
||||
mov word [rmcallstruc0.cs], ax
|
||||
mov word [rmcallstruc0.ds], ax
|
||||
mov word [rmcallstruc0.es], ax
|
||||
mov word [rmcallstruc0.ss], ax
|
||||
pushf
|
||||
pop word [rmcallstruc0.flags] ; setup with RM values
|
||||
|
||||
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 ; alloc DOS memory
|
||||
int 21h
|
||||
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
|
||||
nohandle:
|
||||
cmp cx, 20
|
||||
je .no_handles_duplicated_yet
|
||||
; In this case, there may be some already duplicated
|
||||
; file handles. Switch to the child process and then
|
||||
; terminate it, in order to close the handles.
|
||||
mov word [ es:0Ah ], .half_initialised_child_terminated
|
||||
; set Parent Return Address to ours
|
||||
mov bx, es
|
||||
mov ah, 50h
|
||||
int 21h
|
||||
mov ax, 4C00h
|
||||
int 21h
|
||||
|
||||
.half_initialised_child_terminated:
|
||||
cld
|
||||
.no_handles_duplicated_yet:
|
||||
push cs
|
||||
pop ds
|
||||
mov si, msg.nohandle
|
||||
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
|
||||
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)
|
||||
mov word [ data.pspsel ], es
|
||||
push ds
|
||||
pop es
|
||||
|
||||
mov esi, msg.welcome
|
||||
call printstring
|
||||
|
||||
mov ax, 0303h
|
||||
push cs
|
||||
pop ds
|
||||
mov esi, callback ; ds:esi-> called procedure
|
||||
mov edi, rmcallstruc1 ; es:edi-> (inreentrant) real mode call structure
|
||||
int 31h ; allocate RM callback
|
||||
push ss
|
||||
pop ds
|
||||
jc nocallback
|
||||
mov dword [callrm.callback], edx
|
||||
mov word [callrm.callback+2], cx
|
||||
|
||||
mov ax, 0302h
|
||||
xor ebx, ebx
|
||||
xor ecx, ecx
|
||||
mov edi, rmcallstruc0
|
||||
callingrm:
|
||||
callingrm_ofs equ (callingrm-$$+100h)
|
||||
int 31h ; call RM procedure with interrupt stack
|
||||
jc callrmfailed
|
||||
|
||||
mov ecx, dword [callrm.callback+2]
|
||||
mov edx, dword [callrm.callback]
|
||||
mov ax, 0304h
|
||||
int 31h ; free RM callback
|
||||
mov esi, msg.bye
|
||||
call printstring
|
||||
mov ax, 4C00h ; normal client exit
|
||||
int 21h
|
||||
|
||||
callrmfailed:
|
||||
mov esi, msg.callrmfailed
|
||||
jmp short pmerror
|
||||
nocallback:
|
||||
mov esi, msg.nocallback
|
||||
pmerror:
|
||||
call printstring
|
||||
movzx ecx, word [callrm.callback+2]
|
||||
movzx edx, word [callrm.callback]
|
||||
mov eax, edx
|
||||
and eax, ecx
|
||||
inc ax
|
||||
jnz .done
|
||||
mov ax, 0304h
|
||||
int 31h ; free RM callback
|
||||
.done:
|
||||
mov ax, 4CFFh
|
||||
int 21h
|
||||
|
||||
; This is a RM callback. Called from RM, executed in PM.
|
||||
;
|
||||
; INP: es:edi-> RM call structure
|
||||
; ds:esi-> RM stack
|
||||
; ss:esp-> DPMI host internal stack
|
||||
; CHG: all (?) except es:edi
|
||||
; read/write RM call structure to communicate with RM code
|
||||
callback:
|
||||
push dword [esi]
|
||||
pop dword [es:edi+2Ah] ; set RM cs:ip
|
||||
add word [es:edi+2Eh], byte 4 ; pop 4 byte from the stack
|
||||
; The int3 trap used not to work here in the then-current
|
||||
; lDebugX development version. int 03h however did. Note that
|
||||
; both might crash the DPMI host if no debugger is installed.
|
||||
int strict byte 03h
|
||||
nop
|
||||
int3
|
||||
nop
|
||||
iret
|
||||
|
||||
bits 16
|
||||
|
||||
callrm:
|
||||
; int3 ; int 03h or int3 traps (in real or V86 mode) don't cause any problems.
|
||||
int strict byte 03h
|
||||
xor ax, ax
|
||||
push ax
|
||||
push ax
|
||||
call -1:-1
|
||||
.callback equ $-4
|
||||
pop ax
|
||||
pop ax
|
||||
; int3
|
||||
int strict byte 03h
|
||||
iret
|
||||
|
||||
|
||||
; 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
|
||||
|
||||
|
||||
; The (DPMIed) child process has terminated.
|
||||
childterminated:
|
||||
childterminated_ofs equ (childterminated-$$+100h)
|
||||
cld
|
||||
push cs
|
||||
pop ds
|
||||
mov ah, 4Dh
|
||||
int 21h
|
||||
mov si, msg.backsuccess
|
||||
test al, al
|
||||
jz .success
|
||||
mov si, msg.backerror
|
||||
.success:
|
||||
call printstring
|
||||
mov ax, 4C00h
|
||||
int 21h ; terminate DOS process
|
||||
|
||||
|
||||
align 2
|
||||
data:
|
||||
.pspsel: dw 0
|
||||
|
||||
align 4
|
||||
; This one is used to call down to our RM part
|
||||
; from the 32-bit PM code. All values are filled
|
||||
; in either here or in our RM initialization.
|
||||
rmcallstruc0:
|
||||
.edi: dd 0
|
||||
.esi: dd 0
|
||||
.ebp: dd 0
|
||||
dd 0
|
||||
.ebx: dd 0
|
||||
.edx: dd 0
|
||||
.ecx: dd 0
|
||||
.eax: dd 0
|
||||
.flags: dw 0
|
||||
.es: dw 0
|
||||
.ds: dw 0
|
||||
.fs: dw 0
|
||||
.gs: dw 0
|
||||
.ip: dw callrm
|
||||
.cs: dw 0
|
||||
.sp: dw rmcallsp
|
||||
.ss: dw 0
|
||||
|
||||
align 4
|
||||
; This one is utilized by the DPMI host and
|
||||
; our RM callback when called from RM. All
|
||||
; values are filled in by the DPMI host.
|
||||
rmcallstruc1:
|
||||
.edi: dd 0
|
||||
.esi: dd 0
|
||||
.ebp: dd 0
|
||||
dd 0
|
||||
.ebx: dd 0
|
||||
.edx: dd 0
|
||||
.ecx: dd 0
|
||||
.eax: dd 0
|
||||
.flags: dw 0
|
||||
.es: dw 0
|
||||
.ds: dw 0
|
||||
.fs: dw 0
|
||||
.gs: dw 0
|
||||
.ip: dw 0
|
||||
.cs: dw 0
|
||||
.sp: dw 0
|
||||
.ss: dw 0
|
||||
|
||||
msg:
|
||||
.callrmfailed: asciz "Calling real mode procedure failed.",13,10
|
||||
.bye: asciz "Calling real mode procedure which called callback successful.",13,10
|
||||
.backsuccess: asciz "Child process terminated okay, back in real mode.",13,10
|
||||
.backerror: asciz "Child process terminated with error, back in real mode.",13,10
|
||||
.nocallback: asciz "Could not allocate real mode callback.",13,10
|
||||
.nohost: asciz "No DPMI host installed.",13,10
|
||||
.nohandle: asciz "No free DOS file handle for child process creation.",13,10
|
||||
.nomemory: asciz "Not enough DOS memory for child process creation or client initialization.",13,10
|
||||
.initfailed: asciz "DPMI initialization failed.",13,10
|
||||
.debuginfo: db "Protected mode breakpoint at ",_4digitshex(initsuccessful_ofs),"h.",13,10
|
||||
db "32-bit code segment breakpoint at ",_4digitshex(now32bit_ofs),"h.",13,10
|
||||
db "Real mode procedure called at ",_4digitshex(callingrm_ofs),"h.",13,10
|
||||
db "Return from child process at ",_4digitshex(childterminated_ofs),"h.",13,10
|
||||
asciz 13,10
|
||||
.welcome: asciz "Welcome in 32-bit protected mode.",13,10
|
||||
|
||||
align 2
|
||||
rmcallsp:
|
||||
304
test/ecm/lmacros/jn_warn.mac
Normal file
304
test/ecm/lmacros/jn_warn.mac
Normal file
@@ -0,0 +1,304 @@
|
||||
[list -]
|
||||
%if 0
|
||||
|
||||
Warn for near jumps
|
||||
Public Domain by C. Masloch, 2018
|
||||
|
||||
%endif
|
||||
|
||||
%ifndef __JN_WARN_MAC__
|
||||
%assign __JN_WARN_MAC__ 1
|
||||
|
||||
%imacro jz 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro jnz 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro je 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro jne 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro jc 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro jnc 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro jb 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro jnb 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro ja 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro jna 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro jae 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro jnae 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro jbe 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro jnbe 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
|
||||
%imacro js 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro jns 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%imacro jo 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro jno 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%imacro jp 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro jnp 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
|
||||
%imacro jl 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro jnl 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro jg 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro jng 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro jge 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro jnge 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro jle 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
%imacro jnle 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
|
||||
%imacro jecxz 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%imacro jcxz 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
|
||||
%imacro jmp 1.nolist
|
||||
%%start:
|
||||
%? %1
|
||||
%%end:
|
||||
%assign %%length %%end - %%start
|
||||
%if %%length > 2 && %%length < 5
|
||||
%warning __FILE__ __LINE__ %? %1 %%length
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%endif
|
||||
[list +]
|
||||
984
test/ecm/lmacros/lmacros1.mac
Normal file
984
test/ecm/lmacros/lmacros1.mac
Normal file
@@ -0,0 +1,984 @@
|
||||
[list -]
|
||||
%if 0
|
||||
|
||||
NASM macro collection
|
||||
Public Domain by C. Masloch, 2008-2012
|
||||
Intended for 86 Mode programs.
|
||||
|
||||
%endif
|
||||
|
||||
%ifndef __lMACROS1_MAC__
|
||||
%assign __lMACROS1_MAC__ 1
|
||||
|
||||
%if !!2 - 1
|
||||
%error Unary operator ! returns values > 1. Adjust all sources.
|
||||
%endif
|
||||
|
||||
%idefine by byte
|
||||
%idefine wo word
|
||||
%idefine dwo dword
|
||||
|
||||
%idefine b byte
|
||||
%idefine w word
|
||||
%idefine d dword
|
||||
|
||||
%idefine s short
|
||||
%idefine sho short
|
||||
|
||||
%idefine n near
|
||||
%idefine ne near
|
||||
|
||||
%idefine f far
|
||||
|
||||
|
||||
; Bind major and minor version byte to version word or
|
||||
; version word with exchanged bytes.
|
||||
%idefine ver(major,minor) ( ((major)&0FFh)<<8|((minor)&0FFh) )
|
||||
%idefine verx(major,minor) ( ((minor)&0FFh)<<8|((major)&0FFh) )
|
||||
|
||||
; Macros for table creation
|
||||
%define count(start,stop) stop-start+1
|
||||
%define range(start,stop) (start),count(start,stop) ; generates two comma-separated values
|
||||
|
||||
; Work with 4-byte dates
|
||||
%define date4b(y,m,d) (((y)&0FFFFh)<<16|((m)&0FFh)<<8|((d)&0FFh)) ; create 4-byte date
|
||||
%define year4bd(d) (((d)>>16)&0FFFFh) ; get year from 4-byte date
|
||||
%define month4bd(d) (((d)>>8)&0FFh) ; get month from 4-byte date
|
||||
%define day4bd(d) ((d)&0FFh) ; get day from 4-byte date
|
||||
; The 4-byte date stores the year 0-65535 in the high word,
|
||||
; followed by the month 0-255 in the high byte (of the low
|
||||
; word) and the day 0-255 in the low byte.
|
||||
|
||||
|
||||
; Write numbers as decimal digits into a string
|
||||
%define _1digits_nocheck(d) (((d)% 10)+'0')
|
||||
%xdefine _2digits_nocheck(d) _1digits_nocheck((d)/10),_1digits_nocheck(d)
|
||||
%xdefine _3digits_nocheck(d) _1digits_nocheck((d)/100),_2digits_nocheck(d)
|
||||
%xdefine _4digits_nocheck(d) _1digits_nocheck((d)/1000),_3digits_nocheck(d)
|
||||
%xdefine _5digits_nocheck(d) _1digits_nocheck((d)/10000),_4digits_nocheck(d)
|
||||
%xdefine _1digits(d) (!!(d/10)*(1<<32)+ _1digits_nocheck(d))
|
||||
%xdefine _2digits(d) _1digits((d)/ 10),_1digits_nocheck(d)
|
||||
%xdefine _3digits(d) _1digits((d)/ 100),_2digits_nocheck(d)
|
||||
%xdefine _4digits(d) _1digits((d)/ 1000),_3digits_nocheck(d)
|
||||
%xdefine _5digits(d) _1digits((d)/10000),_4digits_nocheck(d)
|
||||
|
||||
; Write number as decimal digits into a string,
|
||||
; and automagically determine how many digits to use.
|
||||
; The input number is a critical expression to NASM.
|
||||
;
|
||||
; %1 = number to write
|
||||
; %2 = minimal number of digits, 0..5. defaults to 1
|
||||
; (setting it to 0 with a number of 0 writes nothing)
|
||||
%macro _autodigits 1-2.nolist 1
|
||||
%if %2 > 5
|
||||
%error Minimal number of digits 6 or more: %2
|
||||
%endif
|
||||
%if (%1) >= 100000
|
||||
%error Number has to use 6 or more digits: %1
|
||||
%elif (%1) >= 10000 || %2 == 5
|
||||
db _5digits(%1)
|
||||
%elif (%1) >= 1000 || %2 == 4
|
||||
db _4digits(%1)
|
||||
%elif (%1) >= 100 || %2 == 3
|
||||
db _3digits(%1)
|
||||
%elif (%1) >= 10 || %2 == 2
|
||||
db _2digits(%1)
|
||||
%elif (%1) >= 1 || %2 == 1
|
||||
db _1digits(%1)
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
; %1 = name of single-line macro to set. will be prefixed by underscore
|
||||
; %2 = number to write
|
||||
; %3 = minimal number of digits, 0..5. defaults to 1
|
||||
; (setting it to 0 with a number of 0 defines macro to "")
|
||||
%macro _autodigitsdef 2-3.nolist 1
|
||||
%if %3 > 5
|
||||
%error Minimal number of digits 6 or more: %3
|
||||
%endif
|
||||
%if (%2) >= 100000
|
||||
%error Number has to use 6 or more digits: %2
|
||||
%elif (%2) >= 10000 || %3 == 5
|
||||
%define _%1 %[_5digits(%2)]
|
||||
%elif (%2) >= 1000 || %3 == 4
|
||||
%define _%1 %[_4digits(%2)]
|
||||
%elif (%2) >= 100 || %3 == 3
|
||||
%define _%1 %[_3digits(%2)]
|
||||
%elif (%2) >= 10 || %3 == 2
|
||||
%define _%1 %[_2digits(%2)]
|
||||
%elif (%2) >= 1 || %3 == 1
|
||||
%define _%1 %[_1digits(%2)]
|
||||
%elif
|
||||
%define _%1 ""
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
; Write numbers as hexadecimal digits into a string
|
||||
%define _1digitshex_nocheck(h) ( ((h)& 0Fh) + '0' + ('A'-'9'-1)*(((h)& 0Fh)/10) )
|
||||
%xdefine _2digitshex_nocheck(h) _1digitshex_nocheck((h)>> 4),_1digitshex_nocheck(h)
|
||||
%xdefine _3digitshex_nocheck(h) _1digitshex_nocheck((h)>> 8),_2digitshex_nocheck(h)
|
||||
%xdefine _4digitshex_nocheck(h) _1digitshex_nocheck((h)>>12),_3digitshex_nocheck(h)
|
||||
%xdefine _5digitshex_nocheck(h) _1digitshex_nocheck((h)>>16),_4digitshex_nocheck(h)
|
||||
%xdefine _6digitshex_nocheck(h) _1digitshex_nocheck((h)>>20),_5digitshex_nocheck(h)
|
||||
%xdefine _7digitshex_nocheck(h) _1digitshex_nocheck((h)>>24),_6digitshex_nocheck(h)
|
||||
%xdefine _8digitshex_nocheck(h) _1digitshex_nocheck((h)>>28),_7digitshex_nocheck(h)
|
||||
%xdefine _1digitshex(h) (!!(h&~0Fh)*(1<<32)+ _1digitshex_nocheck(h))
|
||||
%xdefine _2digitshex(h) _1digitshex((h)>> 4),_1digitshex_nocheck(h)
|
||||
%xdefine _3digitshex(h) _1digitshex((h)>> 8),_2digitshex_nocheck(h)
|
||||
%xdefine _4digitshex(h) _1digitshex((h)>>12),_3digitshex_nocheck(h)
|
||||
%xdefine _5digitshex(h) _1digitshex((h)>>16),_4digitshex_nocheck(h)
|
||||
%xdefine _6digitshex(h) _1digitshex((h)>>20),_5digitshex_nocheck(h)
|
||||
%xdefine _7digitshex(h) _1digitshex((h)>>24),_6digitshex_nocheck(h)
|
||||
%xdefine _8digitshex(h) _1digitshex((h)>>28),_7digitshex_nocheck(h)
|
||||
|
||||
|
||||
; Compute required words/dwords/qwords/paragraphs/pages/KiB of known byte size (rounds up if necessary)
|
||||
%idefine bytes(b) (b)
|
||||
%idefine words(b) ((b)+1>>1)
|
||||
%idefine dwords(b) ((b)+3>>2)
|
||||
%idefine qwords(b) ((b)+7>>3)
|
||||
%idefine paragraphs(b) ((b)+15>>4)
|
||||
%idefine paras(b) ((b)+15>>4)
|
||||
%idefine pages(b) ((b)+511>>9)
|
||||
%idefine kib(b) ((b)+1023>>10)
|
||||
|
||||
; Compute required bytes of known word/dword/qword/paragraph/page/KiB size
|
||||
%idefine frombytes(b) (b)
|
||||
%idefine fromwords(w) ((w)<<1)
|
||||
%idefine fromdwords(d) ((d)<<2)
|
||||
%idefine fromqwords(q) ((q)<<3)
|
||||
%idefine fromparagraphs(p) ((p)<<4)
|
||||
%idefine fromparas(p) ((p)<<4)
|
||||
%idefine frompages(p) ((p)<<9)
|
||||
%idefine fromkib(k) ((k)<<10)
|
||||
|
||||
|
||||
; Compute relative address
|
||||
;
|
||||
; address: which address is referenced
|
||||
; fixup: how many bytes are added to get the absolute address
|
||||
%define __REL__(address,fixup) ((address)-(fixup))
|
||||
%xdefine __REL8__(address,fixup) __REL__(address,fixup)
|
||||
%xdefine __REL8__(address) __REL__(address,$+1) ; for __JMP_REL8 (db)
|
||||
%xdefine __REL16__(address,fixup) __REL__(address,fixup)
|
||||
%xdefine __REL16__(address) __REL__(address,$+2); for __JMP_REL16 (dw)
|
||||
|
||||
; Values of some opcodes
|
||||
%define __JMP_REL8 0EBh
|
||||
%define __JMP_REL16 0E9h
|
||||
%define __JMP_FAR 0EAh
|
||||
%define __CALL_REL16 0E8h
|
||||
%define __CALL_FAR 9Ah
|
||||
%define __NOP 90h
|
||||
%define __TEST_IMM8 0A8h ; changes flags, NC
|
||||
%define __TEST_IMM16 0A9h ; changes flags, NC
|
||||
; Longer NOPs require two bytes, like a short jump does.
|
||||
; However they execute faster than unconditional jumps.
|
||||
; This one reads random data in the stack segment.
|
||||
; (Search for better ones.)
|
||||
%define __TEST_OFS16_IMM8 0F6h,86h ; changes flags, NC
|
||||
|
||||
; Remove the following macros if already found in other files
|
||||
%unimacro asciz 0-1+.nolist
|
||||
%unimacro ascii 0-1+.nolist
|
||||
%unmacro _ascii_prefix_suffix 2-3+.nolist
|
||||
%undef ascii
|
||||
%undef asciz
|
||||
%undef ascic
|
||||
%undef asciiline
|
||||
%undef ascizline
|
||||
%undef ascicline
|
||||
%unimacro call 1-*.nolist
|
||||
%unimacro verdef 4.nolist
|
||||
%unimacro numdef 1-3.nolist
|
||||
%unimacro strdef 3.nolist
|
||||
%unimacro incdef 2-4.nolist
|
||||
%unimacro _incdef 2-4.nolist
|
||||
%unimacro incdef 2-*.nolist
|
||||
%unimacro excdef 2-*.nolist
|
||||
%unimacro comment 0-*.nolist
|
||||
%unimacro commentif 2.nolist
|
||||
%unimacro commentifn 2.nolist
|
||||
%unimacro fixme 0-1+.nolist
|
||||
%unimacro noparam 0-1+.nolist
|
||||
%unimacro noparamnamed 1-2+.nolist
|
||||
%unimacro assignif 3-4.nolist
|
||||
%unimacro assignifn 3-4.nolist
|
||||
%unimacro cpu 1+.nolist ; removes NASM's standard macro
|
||||
%unimacro selcpu 0.nolist
|
||||
%unimacro cpufailmsg 0-2+.nolist
|
||||
%unimacro check186 0-1.nolist
|
||||
%unimacro check286 0-1.nolist
|
||||
%unimacro check386 0-1.nolist
|
||||
%unimacro check186 0-3.nolist
|
||||
%unimacro check286 0-3.nolist
|
||||
%unimacro check386 0-3.nolist
|
||||
%unimacro checkcpu 1.nolist
|
||||
%unimacro _d 2.nolist
|
||||
%unimacro d0out 0-3.nolist
|
||||
%unimacro endarea 1-2.nolist
|
||||
%unimacro struc 1.nolist ; removes NASM's standard macro
|
||||
%unimacro struc 1-2.nolist
|
||||
%unimacro endstruc 0.nolist ; removes NASM's standard macro
|
||||
%unimacro endstruc 0-1.nolist
|
||||
%unimacro istruc 1.nolist ; removes NASM's standard macro
|
||||
%unimacro at 1-2+.nolist ; removes NASM's standard macro
|
||||
%unimacro iend 0.nolist ; removes NASM's standard macro
|
||||
%unimacro fill 2-3+.nolist
|
||||
%unimacro _fill 3.nolist
|
||||
%unimacro pad 2-3+.nolist
|
||||
%unimacro _pad 3.nolist
|
||||
%unimacro alignd 2-3+.nolist
|
||||
|
||||
|
||||
|
||||
; String prefixed and/or suffixed with sequence(s)
|
||||
;
|
||||
; %1 = prefix
|
||||
; %2 = suffix
|
||||
; %3+ = Optional string
|
||||
%macro _ascii_prefix_suffix 2-3+.nolist
|
||||
db %1
|
||||
%ifnempty %3
|
||||
db %3
|
||||
%endif
|
||||
db %2
|
||||
%endmacro
|
||||
|
||||
%idefine ascii _ascii_prefix_suffix "","",
|
||||
%idefine asciz _ascii_prefix_suffix "",0,
|
||||
%idefine ascic _ascii_prefix_suffix "",36,
|
||||
%idefine asciiline _ascii_prefix_suffix "",{13,10},
|
||||
%idefine ascizline _ascii_prefix_suffix "",{13,10,0},
|
||||
%idefine ascicline _ascii_prefix_suffix "",{13,10,36},
|
||||
|
||||
; Counted string
|
||||
;
|
||||
; %1 = data type of count
|
||||
; %2+ = optional string
|
||||
%imacro _counted 2+.nolist
|
||||
%push
|
||||
%ifnempty %2
|
||||
_strlen %2
|
||||
%1 %$len
|
||||
db %2
|
||||
%else
|
||||
%1 0
|
||||
%endif
|
||||
%pop
|
||||
%endmacro
|
||||
%idefine counted _counted db,
|
||||
%idefine countedb _counted db,
|
||||
%idefine countedw _counted dw,
|
||||
|
||||
%imacro _strlen 1-*.nolist
|
||||
%assign %$len 0
|
||||
%rep %0
|
||||
%ifstr %1
|
||||
%strlen %$addlen %1
|
||||
%assign %$len %$len+%$addlen
|
||||
%elifnempty %1
|
||||
%assign %$len %$len+1
|
||||
%endif
|
||||
%rotate 1
|
||||
%endrep
|
||||
%endmacro
|
||||
|
||||
|
||||
; Call with word parameters behind opcode
|
||||
%imacro call 1-*.nolist
|
||||
%? %1
|
||||
%rep %0 - 1
|
||||
%rotate 1
|
||||
dw %1
|
||||
%endrep
|
||||
%endmacro
|
||||
|
||||
|
||||
; Define version information.
|
||||
;
|
||||
; %1 = Base name of single-line macros
|
||||
; %2 = Additional letter for base name of single-line macros
|
||||
; %3 = Major version
|
||||
; %4 = Minor version
|
||||
;
|
||||
; Created single-line macros:
|
||||
; %1_VER%2 = version word, high byte = major version
|
||||
; %1_VER%2_X = version word (bytes exchanged), high byte = minor version
|
||||
; %1_VER%2_MAJ = Major version number byte
|
||||
; %1_VER%2_MIN = Minor version number byte
|
||||
; %1_VER%2_STR = Up to 7 character version string (maximal "255.255")
|
||||
%imacro verdef 4.nolist
|
||||
%assign %1_VER%2_MAJ %3
|
||||
%assign %1_VER%2_MIN %4
|
||||
%assign %1_VER%2 ver(%3,%4)
|
||||
%assign %1_VER%2_X verx(%3,%4)
|
||||
%push
|
||||
%defstr %$maj %3
|
||||
%defstr %$min %4
|
||||
%if %4 < 10
|
||||
%strcat %$min '0', %$min ; one digit low numbers padded to two digits
|
||||
%endif
|
||||
%strcat %1_VER%2_STR %$maj, '.', %$min
|
||||
%pop
|
||||
%endmacro
|
||||
|
||||
; Define a numeric definition for conditional assembly
|
||||
;
|
||||
; Instead of:
|
||||
; %ifdef DEFINE
|
||||
; use:
|
||||
; %if _DEFINE
|
||||
;
|
||||
; %1 = Definition name. Will get an additional underscore prefix
|
||||
; %2 = Value to assign if not defined (defaults to 0)
|
||||
; %3 = Value to assign if defined, but empty (defaults to 1)
|
||||
%imacro numdef 1-3.nolist 0,1
|
||||
%ifnum __DEFAULTED_%1
|
||||
%if __lMACROS1_MAC__DEFAULTING && __DEFAULTED_%1
|
||||
%undef _%1 ; cause defaulting to take effect again
|
||||
%if __DEFAULTED_%1 == 2 ; defaulted to third parameter?
|
||||
%define _%1 ; cause alternative defaulting to take effect again
|
||||
%endif
|
||||
%endif
|
||||
%endif
|
||||
%ifdef _%1
|
||||
%ifempty _%1
|
||||
%assign _%1 %3 ; i.e. "-d_DEFINE" option on NASM's command line
|
||||
%if __lMACROS1_MAC__DEFAULTING
|
||||
%assign __DEFAULTED_%1 2 ; note alternative defaulting occurred
|
||||
%endif
|
||||
%endif ; (if option was "-d_DEFINE=0", it's left as zero)
|
||||
%if __lMACROS1_MAC__DEFAULTING
|
||||
%ifnnum __DEFAULTED_%1
|
||||
%assign __DEFAULTED_%1 0 ; note no defaulting occurred
|
||||
%endif
|
||||
%endif
|
||||
%else ; If not defined (no option on NASM's command line)
|
||||
%assign _%1 %2 ; then assign to zero
|
||||
%if __lMACROS1_MAC__DEFAULTING
|
||||
%assign __DEFAULTED_%1 1 ; note defaulting occurred
|
||||
%endif
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
|
||||
; Define a generic definition for conditional assembly
|
||||
;
|
||||
; %1 = Definition name. Will get an additional underscore prefix
|
||||
; %2 = What to define as if not defined (defaults to empty)
|
||||
; %3 = What to define as if defined, but empty (defaults to empty)
|
||||
%imacro gendef 1-3.nolist
|
||||
%ifnum __DEFAULTED_%1
|
||||
%if __lMACROS1_MAC__DEFAULTING && __DEFAULTED_%1
|
||||
%undef _%1 ; cause defaulting to take effect again
|
||||
%if __DEFAULTED_%1 == 2 ; defaulted to third parameter?
|
||||
%define _%1 ; cause alternative defaulting to take effect again
|
||||
%endif
|
||||
%endif
|
||||
%endif
|
||||
%ifdef _%1
|
||||
%ifempty _%1
|
||||
%define _%1 %3 ; i.e. "-d_DEFINE" option on NASM's command line
|
||||
%if __lMACROS1_MAC__DEFAULTING
|
||||
%assign __DEFAULTED_%1 2 ; note alternative defaulting occurred
|
||||
%endif
|
||||
%endif ; (if option was "-d_DEFINE=0", it's left as zero)
|
||||
%if __lMACROS1_MAC__DEFAULTING
|
||||
%ifnnum __DEFAULTED_%1
|
||||
%assign __DEFAULTED_%1 0 ; note no defaulting occurred
|
||||
%endif
|
||||
%endif
|
||||
%else ; If not defined (no option on NASM's command line)
|
||||
%define _%1 %2 ; then define to default
|
||||
%if __lMACROS1_MAC__DEFAULTING
|
||||
%assign __DEFAULTED_%1 1 ; note defaulting occurred
|
||||
%endif
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
|
||||
;; %macro __help_strdef_stringify 3+.nolist
|
||||
;;%xdefine _%1 %2%3%2
|
||||
;; %endmacro
|
||||
|
||||
; Define a string definition
|
||||
;
|
||||
; %1 = Definition name. Will get an additional underscore prefix
|
||||
; %2 = Value to assign if not defined
|
||||
; %3 = Value to assign if defined, but no string (if not given, %2)
|
||||
;
|
||||
; Using braces { and } is useful for %2 and %3
|
||||
%imacro strdef 2-3.nolist
|
||||
%ifnum __DEFAULTED_%1
|
||||
%if __lMACROS1_MAC__DEFAULTING && __DEFAULTED_%1
|
||||
%undef _%1 ; cause defaulting to take effect again
|
||||
%if __DEFAULTED_%1 == 2 ; defaulted to third parameter?
|
||||
%define _%1 ; cause alternative defaulting to take effect again
|
||||
%endif
|
||||
%endif
|
||||
%endif
|
||||
%ifdef _%1
|
||||
%ifempty _%1
|
||||
%if %0 >= 3
|
||||
%define _%1 %3 ; i.e. "-d_DEFINE" option on NASM's command line
|
||||
%if __lMACROS1_MAC__DEFAULTING
|
||||
%assign __DEFAULTED_%1 2 ; note alternative defaulting occurred
|
||||
%endif
|
||||
%else
|
||||
%define _%1 %2
|
||||
%if __lMACROS1_MAC__DEFAULTING
|
||||
%assign __DEFAULTED_%1 1 ; note defaulting occurred
|
||||
%endif
|
||||
%endif
|
||||
%else
|
||||
%ifnstr _%1
|
||||
; refer to http://stackoverflow.com/questions/15650276/nasm-convert-integer-to-string-using-preprocessor
|
||||
%defstr _%1 _%1
|
||||
; %define %%quot "
|
||||
; %define %%stringify(x) %%quot %+ x %+ %%quot
|
||||
; %xdefine _%1 %%stringify(_%1)
|
||||
;; __help_strdef_stringify %1, ", _%1
|
||||
; issue: leading and trailing blanks are deleted out of the string.
|
||||
; workaround: quote the content so that it's a string already.
|
||||
; ie this may need nasm source.asm -D_"' content '"
|
||||
%endif
|
||||
%endif ; (if option was "-d_DEFINE='FOO'", it's left as 'FOO')
|
||||
%else ; If not defined (no option on NASM's command line)
|
||||
%define _%1 %2 ;
|
||||
%if __lMACROS1_MAC__DEFAULTING
|
||||
%assign __DEFAULTED_%1 1 ; note defaulting occurred
|
||||
%endif
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
; Include definitions if a condition is met
|
||||
;
|
||||
; %1 = condition
|
||||
; %2-* = included definition name, without underscore prefix
|
||||
%imacro incdef 2-*.nolist
|
||||
%if %1 ; if true
|
||||
%rep (%0 - 1)
|
||||
%rotate 1
|
||||
%assign _%1 1 ; set any included to true
|
||||
%endrep
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
; Exclude definitions if a condition is met
|
||||
;
|
||||
; %1 = condition
|
||||
; %2-* = excluded definition name, without underscore prefix
|
||||
%imacro excdef 2-*.nolist
|
||||
%if %1 ; if true
|
||||
%rep (%0 - 1)
|
||||
%rotate 1
|
||||
%assign _%1 0 ; set any excluded to false
|
||||
%endrep
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%imacro overridedef 1-2.nolist 0
|
||||
%push OVERRIDE
|
||||
%ifdef _%1
|
||||
%assign %$defined 1
|
||||
%else
|
||||
%assign %$defined 0
|
||||
%endif
|
||||
%xdefine %$prior _%1
|
||||
%xdefine %$defaulted __DEFAULTED_%1
|
||||
%define %$name %1
|
||||
%assign _%1 %2
|
||||
%assign __DEFAULTED_%1 0
|
||||
%endmacro
|
||||
|
||||
%imacro resetdef 0-1.nolist
|
||||
%ifnctx OVERRIDE
|
||||
%error Wrong context
|
||||
%endif
|
||||
%if %0
|
||||
%ifnidn %1, %$name
|
||||
%error Wrong usage of resetdef
|
||||
%endif
|
||||
%endif
|
||||
%xdefine _%[%$name] %$prior
|
||||
%xdefine __DEFAULTED_%[%$name] %$defaulted
|
||||
%ifn %$defined
|
||||
%undef _%[%$name]
|
||||
%endif
|
||||
%pop
|
||||
%endmacro
|
||||
|
||||
|
||||
%assign __lMACROS1_MAC__DEFAULTING 0
|
||||
%imacro defaulting 0-1.nolist 1
|
||||
%ifidni %1,toggle
|
||||
%assign __lMACROS1_MAC__DEFAULTING !__lMACROS1_MAC__DEFAULTING
|
||||
%else
|
||||
%assign __lMACROS1_MAC__DEFAULTING !!(%1)
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
numdef _lMACROS1_MAC__CPU_DEFAULTS
|
||||
%if __lMACROS1_MAC__CPU_DEFAULTS
|
||||
numdef 186 ; 186 opcodes allowed
|
||||
numdef 386 ; 386 opcodes allowed
|
||||
incdef _386,186 ; excluding 186 opcodes on 386+ seems not useful
|
||||
%endif
|
||||
|
||||
numdef _lMACROS1_MAC__DEBUG_DEFAULTS
|
||||
%if __lMACROS1_MAC__DEBUG_DEFAULTS
|
||||
numdef DEBUG ; all of the debugging levels
|
||||
incdef _DEBUG,DEBUG0,DEBUG1,DEBUG2,DEBUG3,DEBUG4
|
||||
numdef DEBUG0 ; 1st level debug
|
||||
numdef DEBUG1 ; 2nd level debug
|
||||
numdef DEBUG2 ; 3rd level debug
|
||||
numdef DEBUG3 ; 4th level debug
|
||||
numdef DEBUG4 ; 5th level debug
|
||||
; Note: All specific debug levels are independent.
|
||||
; What each debug level does is program-specific.
|
||||
%endif
|
||||
|
||||
|
||||
; Automatically select CPU depending on definitions
|
||||
;
|
||||
; Use "hardwired" cpu directives instead in source files that
|
||||
; don't include code for multiple CPU generations.
|
||||
%imacro selcpu 0.nolist
|
||||
%if _386
|
||||
cpu 386
|
||||
%elif _186
|
||||
cpu 186
|
||||
%else
|
||||
cpu 8086
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
; Write a message stating what CPU type is required.
|
||||
;
|
||||
; %1 = Part of message before numerical CPU identifier (Defaults to "")
|
||||
; %2 = Part of message after numerical CPU identifier (Defaults to ASCIZ " CPU required")
|
||||
;
|
||||
; Using braces { and } is useful
|
||||
;
|
||||
; No data is generated if assembling for 8086
|
||||
%imacro cpufailmsg 0-2+.nolist "",{" CPU required",0}
|
||||
%if _386
|
||||
db %1, "386", %2
|
||||
%elif _186
|
||||
db %1, "186", %2
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
; Check if required CPU is available
|
||||
;
|
||||
; The code uses cx, ax and comparison flags.
|
||||
;
|
||||
; %1 = Code to execute if CPU requirement not meet
|
||||
%imacro checkcpu 1.nolist
|
||||
%if _386
|
||||
%%checkcpu: check386
|
||||
.fail:
|
||||
%1
|
||||
.pass:
|
||||
%elif _186
|
||||
%%checkcpu: check186
|
||||
.fail:
|
||||
%1
|
||||
.pass:
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
; Check for 186+ CPU
|
||||
;
|
||||
; %1 = true if .fail follows (jump to .pass),
|
||||
; false if .pass follows (jump to .fail)
|
||||
; Default true
|
||||
; %2 = label to jump to if 186+ CPU
|
||||
; Default .pass
|
||||
; %3 = label to jump to if no 186+ CPU
|
||||
; Default .fail
|
||||
%imacro check186 0-3 1 .pass .fail
|
||||
mov cx, 1_0010_0001b ; Shift count's bit 0 and 5 are set; shift data is 1
|
||||
shl ch, cl ; 186+ limits shift count to five bits (0-4): resulting
|
||||
; ch is 10b on 186+; 0 on 8086 (actual result overflows)
|
||||
%if %1
|
||||
jnz %2 ; 186+ -->
|
||||
%else
|
||||
jz %3 ; 8086 -->
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
; Check for 286+ CPU
|
||||
;
|
||||
; %1 = true if .fail follows (jump to .pass),
|
||||
; false if .pass follows (jump to .fail)
|
||||
; Default true
|
||||
; %2 = label to jump to if 286+ CPU
|
||||
; Default .pass
|
||||
; %3 = label to jump to if no 286+ CPU
|
||||
; Default .fail
|
||||
|
||||
%imacro check286 0-3 1 .pass .fail
|
||||
push sp
|
||||
pop ax
|
||||
cmp ax, sp ; 286+ pushes original sp value
|
||||
%if %1
|
||||
je %2 ; 286+ -->
|
||||
%else
|
||||
jne %3 ; 8086 or 186 -->
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
; Check for 386+ CPU
|
||||
;
|
||||
; %1 = true if .fail follows (jump to .pass),
|
||||
; false if .pass follows (jump to .fail)
|
||||
; Default true
|
||||
; %2 = label to jump to if 386+ CPU
|
||||
; Default .pass
|
||||
; %3 = label to jump to if no 386+ CPU
|
||||
; Default .fail
|
||||
%imacro check386 0-3 1 .pass .fail
|
||||
check286 0, {%2}, {%3} ; branches to .fail if 8086 or 186 -->
|
||||
[cpu 286]
|
||||
pushf
|
||||
push wo 0111_0000_0000_0000b
|
||||
popf ; set bits 12-14 of FLAGS ("push imm16" instruction 186+)
|
||||
pushf
|
||||
pop ax ; get what was in FLAGS
|
||||
popf ; restore original flags
|
||||
test ax, 0111_0000_0000_0000b ; 386+ leaves what we pushed; 286 clears bits 12-14 in
|
||||
; Real Mode (Bits 12-14 are used in Protected Mode only)
|
||||
__CPU__
|
||||
%if %1
|
||||
jnz %2 ; 386+ -->
|
||||
%else
|
||||
jz %3 ; 286 -->
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
; Instruction if specified level debug definition true
|
||||
;
|
||||
; %1 = Level of requested debug definition
|
||||
; %2 = Instruction
|
||||
%imacro _d 2+.nolist
|
||||
%if _DEBUG%1
|
||||
%2
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
; Instruction if xth level debug definition true
|
||||
;
|
||||
; %1+ = Instruction
|
||||
%idefine d0 _d 0,
|
||||
%idefine d1 _d 1,
|
||||
%idefine d2 _d 2,
|
||||
%idefine d3 _d 3,
|
||||
%idefine d4 _d 4,
|
||||
|
||||
; Breakpoint if xth level debug definition true
|
||||
%idefine dbp _d, __DEBUG_BP__
|
||||
%idefine d0bp d0 __DEBUG0_BP__
|
||||
%idefine d1bp d1 __DEBUG1_BP__
|
||||
%idefine d2bp d2 __DEBUG2_BP__
|
||||
%idefine d3bp d3 __DEBUG3_BP__
|
||||
%idefine d4bp d4 __DEBUG4_BP__
|
||||
%idefine dd0bp _d,{d0bp}
|
||||
%idefine dd1bp _d,{d1bp}
|
||||
%idefine dd2bp _d,{d2bp}
|
||||
%idefine dd3bp _d,{d3bp}
|
||||
%idefine dd4bp _d,{d4bp}
|
||||
|
||||
; Breakpoint instructions for all debug levels
|
||||
;
|
||||
; "call ...", "int1", "int3" are useful single-instruction breakpoints
|
||||
%define __DEBUG_BP__ int3
|
||||
%define __DEBUG0_BP__ int3
|
||||
%define __DEBUG1_BP__ int3
|
||||
%define __DEBUG2_BP__ int3
|
||||
%define __DEBUG3_BP__ int3
|
||||
%define __DEBUG4_BP__ int3
|
||||
|
||||
; Print number if 1st level debug definition true
|
||||
;
|
||||
; %1 = Number to display (in range 0-255)
|
||||
; %2 = Override call for "call __DEBUG0_DISPALDEC__"
|
||||
; %3 = Override call for "call __DEBUG0_DISPAL__"
|
||||
;
|
||||
; The calls have to preserve all registers except ax.
|
||||
%macro d0out 0-3.nolist -,call __DEBUG0_DISPALDEC__,call __DEBUG0_DISPAL__
|
||||
%if _DEBUG0
|
||||
push ax
|
||||
%ifnidn %1,-
|
||||
mov al, %1
|
||||
%2
|
||||
%endif
|
||||
mov al, '.'
|
||||
%3
|
||||
pop ax
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
; cpu macro replacement
|
||||
;
|
||||
; __CPU__ = like __SECT__ (that is, use [cpu ...] and switch back with __CPU__)
|
||||
%imacro cpu 1+.nolist
|
||||
%define __CPU__ [cpu %1]
|
||||
__CPU__
|
||||
%endmacro
|
||||
|
||||
; Define size of area
|
||||
;
|
||||
; %1 = Starting label of area
|
||||
; %2 = if true, use already defined label %1_size (for
|
||||
; use with NASM's struc and endstruc); default is false
|
||||
;
|
||||
; Provided labels: %1_size (byte), %1_size_b (same),
|
||||
; %1_size_w (words), %1_size_d (dwords), %1_size_q
|
||||
; (qwords), %1_size_p (paragraphs), %1_size_pg (pages),
|
||||
; %1_size_ki (KiB).
|
||||
%imacro endarea 1-2.nolist 0
|
||||
%ifn %2
|
||||
%1_size equ $ - %1
|
||||
%endif
|
||||
%1_size_b equ %1_size
|
||||
%1_size_w equ %1_size+1 >>1
|
||||
%1_size_d equ %1_size+3 >>2
|
||||
%1_size_q equ %1_size+7 >>3
|
||||
%1_size_p equ %1_size+15 >>4
|
||||
%1_size_pg equ %1_size+511 >>9
|
||||
%1_size_ki equ %1_size+1023 >>10
|
||||
%endmacro
|
||||
|
||||
; The following macros replace NASM's standard struc macros.
|
||||
; They add support for non-zero starting offsets of structures
|
||||
; (optional %2 of struc is starting offset) and creating endarea's
|
||||
; various size definitions with endstruc (if optional %1 of endstruc
|
||||
; is true).
|
||||
%imacro struc 1-2.nolist 0
|
||||
%push
|
||||
%define %$strucname %1
|
||||
[absolute %2]
|
||||
%$strucname:
|
||||
%endmacro
|
||||
%imacro endstruc 0-1.nolist 0
|
||||
%if %1
|
||||
endarea %$strucname
|
||||
%else
|
||||
%{$strucname}_size equ ($-%$strucname)
|
||||
%endif
|
||||
%pop
|
||||
__SECT__
|
||||
%endmacro
|
||||
|
||||
%imacro istruc 1.nolist
|
||||
%push
|
||||
%define %$strucname %1
|
||||
%$strucstart:
|
||||
%endmacro
|
||||
%imacro at 1-2+.nolist
|
||||
times (%1-%$strucname)-($-%$strucstart) db 0
|
||||
%2
|
||||
%endmacro
|
||||
%imacro iend 0.nolist
|
||||
times %{$strucname}_size-($-%$strucstart) db 0
|
||||
%pop
|
||||
%endmacro
|
||||
|
||||
|
||||
%imacro checkeven 1.nolist
|
||||
%ifnnum %1
|
||||
%error Number expected
|
||||
%endif
|
||||
%if %1 & 1
|
||||
%error Even value expected
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
; Fill data to specified byte size (specify data)
|
||||
;
|
||||
; %1 = Byte size to fill
|
||||
; %2 = Fill byte (eg 0, 90h, 32)
|
||||
; %3 = Actual instruction to place before filling (optional)
|
||||
%imacro fill 2-3+.nolist
|
||||
%%data:
|
||||
%3 ; Actual data if given, else expands to nothing
|
||||
_fill %1,%2,%%data
|
||||
%endmacro
|
||||
|
||||
|
||||
; Fill data to specified byte size (specify start label of data)
|
||||
;
|
||||
; %1 = Byte size to fill
|
||||
; %2 = Fill byte (eg 0, 90h, 32)
|
||||
; %3 = Start label of data
|
||||
%imacro _fill 3.nolist
|
||||
%if %1 < 0 ; hu?
|
||||
%error _fill: size is negative
|
||||
%elif $-(%3) < 0 ; should generate a phase error anyway. however
|
||||
%error _fill: start label must not be behind filling
|
||||
%elif (%1)-$+(%3) < 0 ; times would also throw an error then ("TIMES value x is negative")
|
||||
%error _fill: data too large, exceeds size ; but showing a more specific message won't hurt
|
||||
%endif
|
||||
times (%1)-$+(%3) db %2 ; Fill with fill byte to given size
|
||||
%endmacro
|
||||
|
||||
|
||||
; Pad data to specified byte boundary (specify data)
|
||||
;
|
||||
; Does _not_ pad relative to the section start but relative to the start of the data. Use
|
||||
; NASM's align (or alignd below) to pad (cough, cough... align) if you don't want that.
|
||||
;
|
||||
; %1 = Byte boundary
|
||||
; %2 = Pad byte (eg 0, 90h, 32)
|
||||
; %3 = Actual instruction to place before padding (optional, else macro creates nothing)
|
||||
%imacro pad 2-3+.nolist
|
||||
%%data:
|
||||
%3 ; Actual data if given, else expands to nothing
|
||||
_pad %1,%2,%%data
|
||||
%endmacro
|
||||
|
||||
|
||||
; Pad data to specified byte boundary (specify start label of data)
|
||||
;
|
||||
; Does _not_ pad relative to the section start but relative to the start of the data. Use
|
||||
; NASM's align (or alignd below) to pad (cough, cough... align) if don't you want that.
|
||||
;
|
||||
; %1 = Byte boundary
|
||||
; %2 = Pad byte (eg 0, 90h, 32)
|
||||
; %3 = Start label of data (if it equals $, no padding is created)
|
||||
%imacro _pad 3.nolist
|
||||
%if %1 < 0 ; hu?
|
||||
%error _pad: boundary is negative
|
||||
%elifn %1
|
||||
%error _pad: boundary is zero
|
||||
%elif $-%3 < 0 ; should generate a phase error anyway. however
|
||||
%error _pad: start label must not be behind padding
|
||||
%endif
|
||||
|
||||
%push
|
||||
%assign %$size $-%3 ; size of data
|
||||
%assign %$count 0
|
||||
%rep %1 ; must be at the boundary then
|
||||
%ifn (%$count + %$size) % %1 ; if ( !((paddedcount + size) modulo boundary) )
|
||||
%exitrep ; then { we're done }
|
||||
%endif
|
||||
%assign %$count %$count+1 ; else keep counting
|
||||
%endrep
|
||||
%if %$count >= %1 ; if loop end caused by %endrep
|
||||
%error pad: an unknown error occured
|
||||
%endif
|
||||
times (%$count-1) db %2 ; put this data for the assembler. but in _one_ line
|
||||
%pop
|
||||
%endmacro
|
||||
|
||||
|
||||
; Similar to pad but using align:
|
||||
;
|
||||
; %1 = Byte boundary (relative from the section start)
|
||||
; %2 = Align byte (eg 0, 90h, 32)
|
||||
; %3 = Actual instruction to place before aligning (optional, else macro still aligns!)
|
||||
%imacro alignd 2-3+.nolist
|
||||
%3
|
||||
%if %1 < 0 ; hu?
|
||||
%error alignd: boundary is negative
|
||||
%elifn %1
|
||||
%error alignd: boundary is zero
|
||||
%else
|
||||
align %1,db %2
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
|
||||
; Comment a line out
|
||||
;
|
||||
; Purpose: In front of instructions that are only to be created if
|
||||
; an expressions results in true, place a valid single-line
|
||||
; macro name. Then use the commentif (or commentifn) macro
|
||||
; below to create a single-line macro from an expression.
|
||||
;
|
||||
; This will use the macro "comment" so the instructions won't
|
||||
; assemble if the expression doesn't result in true. (Or
|
||||
; if the expression doesn't result in false, in case of
|
||||
; commentifn.) Else the single-line macro gets defined to
|
||||
; nothing causing the instructions to assemble. This "comment"
|
||||
; macro is required because you can't define a single-line
|
||||
; macro to the value of semicolon (";") with the preprocessor
|
||||
; for obvious reasons. (It will think a real comment starts
|
||||
; with the semicolon ;)
|
||||
%imacro comment 0-*.nolist
|
||||
%endmacro
|
||||
|
||||
|
||||
; Specify a single-line macro for above comment macro purpose
|
||||
;
|
||||
; %1 = expression (gets placed after %if)
|
||||
; %2 = single-line macro (gets underscore prefix)
|
||||
%imacro commentif 2.nolist
|
||||
%if %1
|
||||
%define _%2 comment ; define it to invoke comment
|
||||
%else
|
||||
%define _%2 ; define it to a zero string
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
|
||||
; Like commentif, but expression gets placed after %ifn
|
||||
%imacro commentifn 2.nolist
|
||||
%ifn %1
|
||||
%define _%2 comment ; define it to invoke comment
|
||||
%else
|
||||
%define _%2 ; define it to a zero string
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
|
||||
; Aborts assembling if found anywhere within source
|
||||
%imacro fixme 0-1+.nolist
|
||||
%error %1
|
||||
%endmacro
|
||||
|
||||
|
||||
; Comment a line out but make sure there are no parameters
|
||||
%imacro noparam 0-1+.nolist
|
||||
%if %0
|
||||
%error Must not have parameters.
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
|
||||
; Like noparam, except %1 is a message to display
|
||||
%imacro noparamnamed 1-2+.nolist
|
||||
%ifnempty %2
|
||||
%error %1: Must not have parameters.
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
|
||||
; Assign a single-line macro to an value if a condition matches, else zero
|
||||
;
|
||||
; %1 = expression (gets placed after %if)
|
||||
; %2 = single-line macro (gets underscore prefix)
|
||||
; %3 = value if condition matches
|
||||
; %4 = value if condition doesn't match (default: 0)
|
||||
%imacro assignif 3-4.nolist 0
|
||||
%if %1
|
||||
%assign _%2 %3
|
||||
%else
|
||||
%assign _%2 %4
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
|
||||
; Like assignif, but expression gets placed after %ifn
|
||||
%imacro assignifn 3-4.nolist 0
|
||||
%ifn %1
|
||||
%assign _%2 %3
|
||||
%else
|
||||
%assign _%2 %4
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
%endif
|
||||
[list +]
|
||||
1207
test/ecm/lmacros/lmacros2.mac
Normal file
1207
test/ecm/lmacros/lmacros2.mac
Normal file
File diff suppressed because it is too large
Load Diff
121
test/ecm/lmacros/lmacros3.mac
Normal file
121
test/ecm/lmacros/lmacros3.mac
Normal file
@@ -0,0 +1,121 @@
|
||||
[list -]
|
||||
%if 0
|
||||
|
||||
Extensions to NASM macro collection
|
||||
Public Domain by C. Masloch, 2019
|
||||
Intended for 86 Mode programs.
|
||||
|
||||
%endif
|
||||
|
||||
%ifndef __lMACROS3_MAC__
|
||||
%assign __lMACROS3_MAC__ 1
|
||||
|
||||
%include "lmacros2.mac"
|
||||
[list -]
|
||||
|
||||
|
||||
; This macro is used to generate byte or word access to specific
|
||||
; bits of a dword variable.
|
||||
; %1 = token: if "~", bit value will be negated after checking
|
||||
; %2 = instruction
|
||||
; %3 = variable, in the form "[address]" without size specification
|
||||
; %4 = bit value(s) to access
|
||||
; %5 = bool: disable word access warning, defaults to 0
|
||||
; If the value in %4 has bits set in different bytes so that
|
||||
; a single 8- or 16-bit instruction cannot access all the bits,
|
||||
; an error is displayed. This insures that the macro only has
|
||||
; to generate one instruction, as a 32-bit access on 16-bit
|
||||
; CPUs requires multiple instructions. This workaround code
|
||||
; needs to be written specifically then, or the flags have to
|
||||
; be re-ordered to allow the access.
|
||||
%macro _opt 4-5.nolist 0
|
||||
%push
|
||||
%defstr %$adr %3
|
||||
%strlen %$len %$adr
|
||||
%substr %$tf %$adr 1
|
||||
%substr %$tb %$adr %$len
|
||||
%substr %$adr %$adr 2,-2
|
||||
%deftok %$adr %$adr
|
||||
%%num: equ %4
|
||||
%assign %$num %%num
|
||||
%ifnidn %$tf,"["
|
||||
%error Invalid memory access syntax
|
||||
%elifnidn %$tb,"]"
|
||||
%error Invalid memory access syntax
|
||||
%elifn %$num
|
||||
%error Bit value is zero! Check code.
|
||||
%elifn (%$num) & ~0FFh
|
||||
%2 byte [%$adr], %1(%$num)
|
||||
%elifn (%$num) & ~0FF00h
|
||||
%2 byte [%$adr+1], %1((%$num)>>8)
|
||||
%elifn (%$num) & ~0FF0000h
|
||||
%2 byte [%$adr+2], %1((%$num)>>16)
|
||||
%elifn (%$num) & ~0FF000000h
|
||||
%2 byte [%$adr+3], %1((%$num)>>24)
|
||||
%elifn (%$num) & ~0FFFFh
|
||||
%ifn %5
|
||||
%warning Macro generated word access
|
||||
%endif
|
||||
%2 word [%$adr], %1(%$num)
|
||||
%elifn (%$num) & ~0FFFF00h
|
||||
%ifn %5
|
||||
%warning Macro generated word access
|
||||
%endif
|
||||
%2 word [%$adr+1], %1((%$num)>>8)
|
||||
%elifn (%$num) & ~0FFFF0000h
|
||||
%ifn %5
|
||||
%warning Macro generated word access
|
||||
%endif
|
||||
%2 word [%$adr+2], %1((%$num)>>16)
|
||||
%else
|
||||
%error Unsupported macro usage, requires dword:
|
||||
%ifempty %1
|
||||
%error %2 dword [%$adr], %$num
|
||||
%else
|
||||
%error %2 dword [%$adr], %1(%$num)
|
||||
%endif
|
||||
%endif
|
||||
%pop
|
||||
%endmacro
|
||||
|
||||
; User forms for above macro.
|
||||
; %1 = variable, in the form "[address]" without size specification
|
||||
; %2 = bit value(s) to access
|
||||
; %3 = bool: disable word access warning, defaults to 0
|
||||
; testopt tests the specified bits (using a "test" instruction) and
|
||||
; leaves a meaningful result in ZF, as well as NC.
|
||||
; clropt clears the specified bits (using an "and" instruction with
|
||||
; the negated value) and leaves NC, but a random ZF.
|
||||
; setopt sets the specified bits (using an "or" instruction) and
|
||||
; leaves NC, NZ.
|
||||
; xoropt toggles the specified bits (using a "xor" instruction) and
|
||||
; leaves NC, but a random ZF.
|
||||
%idefine testopt _opt ,test,
|
||||
%idefine clropt _opt ~,and,
|
||||
%idefine setopt _opt ,or,
|
||||
%idefine xoropt _opt ,xor,
|
||||
|
||||
|
||||
%imacro addsection 1-2.nolist
|
||||
%ifdef _SECTION_ADDED_%1
|
||||
%error Section %1 already added
|
||||
%endif
|
||||
section %1 %2
|
||||
%define _SECTION_ADDED_%1 1
|
||||
usesection %1
|
||||
%endmacro
|
||||
|
||||
%imacro usesection 1-2.nolist 0
|
||||
%ifndef _SECTION_ADDED_%1
|
||||
%error Section %1 not yet added
|
||||
%endif
|
||||
%if %2
|
||||
[section %1]
|
||||
%else
|
||||
section %1
|
||||
%endif
|
||||
%endmacro
|
||||
|
||||
|
||||
%endif
|
||||
[list +]
|
||||
Reference in New Issue
Block a user