📄 biosmem.asm
字号:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; demo of BIOS calls that check memory size
; This code is public domain (no copyright).
; You can do whatever you want with it.
;
; This code uses these interrupts:
; 1. INT 15h AX=E820h (32-bit CPU only)
; 2. INT 15h AX=E801h
; 3. INT 15h AH=88h
; 4. INT 12h
;
; assemble with NASM: nasm -f bin -o biosmem.com biosmem.asm
;
; rewritten Sep 6, 2001
; - trying to handle possible BIOS bugs per Ralf Brown's list and
; http://marc.theaimsgroup.com/?l=linux-kernel&m=99322719013363&w=2
; - now displaying memory block info, instead of just a size value
; - new wrnum function
;
; xxx - INT 15h AX=E820h shows a 1K block at the top of conventional
; memory for my system -- is this reserved for the EBDA?
; I don't _have_ an EBDA, at least, not with my current CMOS settings...
; will the 1K block disappear if the EBDA is enabled?
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ORG 100h
mov si,mem_msg
call cputs
; check for 32-bit CPU
call cpu_is_32bit
; try INT 15h AX=E820h
or ah,ah
je cant_e820
call extmem_int15_e820
jnc ok
; before trying other BIOS calls, use INT 12h to get conventional memory size
cant_e820:
int 12h
; convert from K in AX to bytes in CX:BX
xor ch,ch
mov cl,ah
mov bh,al
xor bl,bl
shl bx,1
rcl cx,1
shl bx,1
rcl cx,1
; set range base (in DX:AX) to 0 and display it
xor dx,dx
xor ax,ax
call display_range
; try INT 15h AX=E801h
call extmem_int15_e801
jnc ok
; try INT 15h AH=88h
call extmem_int15_88
jnc ok
; uh-oh
mov si,err_msg
call cputs
; exit to DOS
ok:
mov ax,4C00h
int 21h
got_32bit_cpu:
db 0
mem_msg:
db "Memory ranges:"
crlf_msg:
db 13, 10, 0
base_msg:
db " base=0x", 0
size_msg:
db ", size=0x", 0
err_msg:
db "*** All BIOS calls to determine extended memory size "
db "have failed ***", 13, 10, 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: cpu_is_32bit
; action: checks for 32-bit CPU
; in: (nothing)
; out: AH != 0 if 32-bit CPU
; modifies: AX
; minimum CPU: 8088
; notes: C prototype: extern int cpu_is_32bit(void);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
cpu_is_32bit:
push bx
pushf
pushf
pop bx ; old FLAGS -> BX
mov ax,bx
xor ah,70h ; try changing b14 (NT)...
push ax ; ... or b13:b12 (IOPL)
popf
pushf
pop ax ; new FLAGS -> AX
popf
xor al,al ; zero AL
xor ah,bh ; 32-bit CPU if we changed NT...
and ah,70h ; ...or IOPL
pop bx
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: display_range
; action: printf("base=0x%lX, size=0x%lX\n", DX:AX, CX:BX);
; in: (nothing)
; out: (nothing)
; modifies: (nothing)
; minimum CPU: 8088
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
display_range:
push si
push dx
push bx
push ax
; if size==0, do nothing
mov si,cx
or si,bx
je display_range_1
mov si,base_msg
call cputs
push bx
mov bx,16
call wrnum
mov si,size_msg
call cputs
pop ax
mov dx,cx
call wrnum
mov si,crlf_msg
call cputs
display_range_1:
pop ax
pop bx
pop dx
pop si
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: extmem_int15_e820
; action: gets extended memory info using INT 15h AX=E820h
; in: (nothing)
; out: (nothing)
; modifies: (nothing)
; minimum CPU: 386+
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
buffer_e820:
times 20h db 0
buffer_e820_len equ $ - buffer_e820
extmem_int15_e820:
push es
push di
push edx
push ecx
push ebx
push eax
push ds
pop es
mov di,buffer_e820
xor ebx,ebx ; INT 15h AX=E820h continuation value
mov edx,534D4150h ; "SMAP"
mov ecx,buffer_e820_len
mov eax,0000E820h
int 15h
; CY=1 on first call to INT 15h AX=E820h is an error
jc extmem_e820_4
extmem_e820_1:
cmp eax,534D4150h ; "SMAP"
; return EAX other than "SMAP" is an error
stc
jne extmem_e820_4
cmp dword [es:di + 16],1 ; type 1 memory (available to OS)
jne extmem_e820_2
push bx
mov ax,[es:di + 0] ; base
mov dx,[es:di + 2]
mov bx,[es:di + 8] ; size
mov cx,[es:di + 10]
call display_range
pop bx
extmem_e820_2:
or ebx,ebx
je extmem_e820_3
; "In addition the SMAP signature is restored each call, although not
; required by the specification in order to handle some known BIOS bugs."
; -- http://marc.theaimsgroup.com/?l=linux-kernel&m=99322719013363&w=2
mov edx,534D4150h ; "SMAP"
mov ecx,buffer_e820_len
mov eax,0000E820h
int 15h
; "the BIOS is permitted to return a nonzero continuation value in EBX
; and indicate that the end of the list has already been reached by
; returning with CF set on the next iteration"
; -- b-15E820 in Ralf Brown's list
jnc extmem_e820_1
extmem_e820_3:
clc
extmem_e820_4:
pop eax
pop ebx
pop ecx
pop edx
pop di
pop es
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: extmem_int15_e801
; action: gets extended memory size using INT 15h AX=E801h
; in: (nothing)
; out: (nothing)
; modifies: (nothing)
; minimum CPU: 8088 for code, 286+ for extended memory
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
extmem_int15_e801:
push dx
push cx
push bx
push ax
mov ax,0E801h
; "...the INT 15 AX=0xE801 service is called and the results are sanity
; checked. In particular the code zeroes the CX/DX return values in order
; to detect BIOS implementations that do not set the usable memory data.
; It also handles older BIOSes that return AX/BX but not AX/BX data." (?)
; -- http://marc.theaimsgroup.com/?l=linux-kernel&m=99322719013363&w=2
xor dx,dx
xor cx,cx
int 15h
jc extmem_e801_2
mov si,ax
or si,bx
jne extmem_e801_1
mov ax,cx
mov bx,dx
extmem_e801_1:
push bx
; convert from Kbytes in AX to bytes in CX:BX
xor ch,ch
mov cl,ah
mov bh,al
xor bl,bl
shl bx,1
rcl cx,1
shl bx,1
rcl cx,1
; set range base (in DX:AX) to 1 meg and display it
mov dx,10h
xor ax,ax
call display_range
; convert stacked value from 64K-blocks to bytes in CX:BX
pop cx
xor bx,bx
; set range base (in DX:AX) to 16 meg and display it
mov dx,100h
xor ax,ax
call display_range
extmem_e801_2:
pop ax
pop bx
pop cx
pop dx
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: extmem_int15_88
; action: gets extended memory size using INT 15h AH=88h
; in: (nothing)
; out: (nothing)
; modifies: (nothing)
; minimum CPU: 8088 for code, 286+ for extended memory
; notes: HIMEM.SYS will hook this interrupt and make
; it return 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
extmem_int15_88:
push dx
push cx
push bx
push ax
mov ax,8855h
int 15h
; "not all BIOSes correctly return the carry flag, making this call
; unreliable unless one first checks whether it is supported through
; a mechanism other than calling the function and testing CF"
; -- b-1588 in Ralf Brown's list
; test if AL register modified by INT 15h AH=88h
cmp al,55h
jne extmem_int15_1
mov ax,88AAh
int 15h
cmp al,0AAh
stc
je extmem_int15_2
; convert from Kbytes in AX to bytes in CX:BX
extmem_int15_1:
xor ch,ch
mov cl,ah
mov bh,al
xor bl,bl
shl bx,1
rcl cx,1
shl bx,1
rcl cx,1
; set base to 1 meg and display range
mov dx,10h
xor ax,ax
call display_range
extmem_int15_2:
pop ax
pop bx
pop cx
pop dx
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: cputs
; action: writes 0-terminated string to screen
; in: SI -> string
; out: (nothing)
; modifies: (nothing)
; minimum CPU: 8088
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
cputs:
push si
push bx
push ax
cld ; string operations go up
mov ah,0Eh ; INT 10h: teletype output
xor bx,bx ; video page 0
jmp short cputs_2
cputs_1:
int 10h
cputs_2:
lodsb
or al,al
jne cputs_1
pop ax
pop bx
pop si
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: wrnum
; action: writes 32-bit value to text screen
; in: 32-bit unsigned value in DX:AX, radix in BX
; out: (nothing)
; modifies: (nothing)
; minimum CPU: 8088
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
times 40 db 0
num_buf:
db 0
wrnum:
push si
push dx
push cx
push bx
push ax
mov si,num_buf
; extended precision division from section 9.3.5
; of Randall Hyde's "Art of Assembly"
; start: DX=dividend MSW, AX=dividend LSW, BX=divisor
wrnum1:
push ax
mov ax,dx
xor dx,dx
; before div: DX=0, AX=dividend MSW, BX=divisor
; after div: AX=quotient MSW, DX=intermediate remainder
div bx
mov cx,ax
pop ax
; before div: DX=intermediate remainder, AX=dividend LSW, BX=divisor
; after div: AX=quotient LSW, DX=remainder
div bx
; end: DX=quotient MSW, AX=quotient LSW, CX=remainder
xchg dx,cx
add cl,'0'
cmp cl,'9'
jbe wrnum2
add cl,('A'-('9'+1))
wrnum2:
dec si
mov [si],cl
mov cx,ax
or cx,dx
jne wrnum1
call cputs
pop ax
pop bx
pop cx
pop dx
pop si
ret
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -