flat assembler
Message board for the users of flat assembler.

Index > DOS > Checking for PCI BIOS

Author
Thread Post new topic Reply to topic
Cyril



Joined: 02 Jan 2013
Posts: 4
Location: Kryvyi Rih, Ukraine
Cyril
Hello, I'm only starting to learn assembly and have a great desire to learn Smile. I've written a program that checks if a computer has PCI BIOS installed. Here's the code:

Code:
use32
org 100h

        mov eax, 0000B101h      ; check for PCI BIOS presence
        sub edi, edi            ; why?
        int 1Ah                       ; time of the day AND PCI functions?
        cmp edi, ' ICP'         ; EDI has 'PCI ' in reverse
        je next                 ; success -> proceed
        stc                     ; set carry flag to 1 (=error)
        call error                            ; error handling

next:
        mov ah, 9
        mov dx, successmsg      ; print success message
        int 21h
        int 20h

error:
        mov ah, 9
        mov dx, errormsg        ; print error message
        int 21h
        int 20h

successmsg      db      'PCI BIOS found!', 24h
errormsg        db      'PCI BIOS not found!', 24h
    


But it prints nothing... It just exits, and that's all. What's wrong? I don't have the slightest idea...
BTW: I found that removing "org 100h" has no effect at all.

My system is: Eee PC 701, FreeDOS 1.1, HimemX.

Thank you for help.
Post 17 Jan 2013, 17:30
View user's profile Send private message Visit poster's website Reply with quote
LocoDelAssembly
Your code has a bug


Joined: 06 May 2005
Posts: 4633
Location: Argentina
LocoDelAssembly
Change ' ICP' to 'PCI ' (fasm already reverses the string for you), and replace 24h with 24h, 0dh, 0ah (just in case FreeDOS is having some sort of line buffering which is preventing your prints to be shown). "call error" should probably be "jmp error" (but shouldn't be the reason for the failure)
Post 17 Jan 2013, 18:03
View user's profile Send private message Reply with quote
shutdownall



Joined: 02 Apr 2010
Posts: 518
Location: Munich
shutdownall
Cyril wrote:
Hello, I'm only starting to learn assembly and have a great desire to learn Smile. I've written a program that checks if a computer has PCI BIOS installed. Here's the code:

Code:
use32
org 100h

        mov eax, 0000B101h      ; check for PCI BIOS presence
        sub edi, edi            ; why?
        int 1Ah                       ; time of the day AND PCI functions?
        cmp edi, ' ICP'         ; EDI has 'PCI ' in reverse
        je next                 ; success -> proceed
        stc                     ; set carry flag to 1 (=error)
        call error                            ; error handling

next:
        mov ah, 9
        mov dx, successmsg      ; print success message
        int 21h
        int 20h

error:
        mov ah, 9
        mov dx, errormsg        ; print error message
        int 21h
        int 20h

successmsg      db      'PCI BIOS found!', 24h
errormsg        db      'PCI BIOS not found!', 24h
    


But it prints nothing... It just exits, and that's all. What's wrong? I don't have the slightest idea...
BTW: I found that removing "org 100h" has no effect at all.

My system is: Eee PC 701, FreeDOS 1.1, HimemX.

Thank you for help.


First look here
http://www.ctyme.com/intr/rb-2371.htm

and here
http://www.ctyme.com/rbrown.htm

for more information about DOS/BIOS interrupts, what they need and what they give back.

And the string 'PCI' is delivered back in edx and not edi.
You may compare to the following value instead of using strings if you are not familar with them:
EDX = 20494350h (' ICP')
Post 17 Jan 2013, 18:50
View user's profile Send private message Send e-mail Reply with quote
freecrac



Joined: 19 Oct 2011
Posts: 117
Location: Germany Hamburg
freecrac
use32 is not a good choice for 16 bit software interrupts.
And "use32" is only highly recommended if we use the 32 bit mode and the "D"-Flag is already set before.

The only one difference between the 16 bit mode and the 32 bit mode is the consideration and meaning of the adresssize- and the operandsize prefix and how our cpu is interpreting an opcode with or without those prefixes.
Quote:

Intel:

Instruction prefixes can be used to override the default operand size and address size of a code segment. These prefixes can be used in real-address mode as well as in protected mode and virtual-8086 mode. An operand-size or address-size prefix only changes the size for the duration of the instruction.

The following two instruction prefixes allow mixing of 32-bit and 16-bit operations within one segment:
•The operand-size prefix (66H)
•The address-size prefix (67H)

These prefixes reverse the default size selected by the D flag in the code-segment descriptor. For example, the processor can interpret the (MOV mem, reg) instruction in any of four ways:
•In a 32-bit code segment:
—Moves 32 bits from a 32-bit register to memory using a 32-bit effective address.
—If preceded by an operand-size prefix, moves 16 bits from a 16-bit register to memory using a 32-bit effective address.
—If preceded by an address-size prefix, moves 32 bits from a 32-bit register to memory using a 16-bit effective address.
—If preceded by both an address-size prefix and an operand-size prefix, moves 16 bits from a 16-bit register to memory using a 16-bit effective address.

•In a 16-bit code segment:
—Moves 16 bits from a 16-bit register to memory using a 16-bit effective address.
—If preceded by an operand-size prefix, moves 32 bits from a 32-bit register to memory using a 16-bit effective address.
—If preceded by an address-size prefix, moves 16 bits from a 16-bit register to memory using a 32-bit effective address.
—If preceded by both an address-size prefix and an operand-size prefix, moves 32 bits from a 32-bit register to memory using a 32-bit effective address.

The previous examples show that any instruction can generate any combination of operand size and address size regardless of whether the instruction is in a 16- or 32-bit segment. The choice of the 16- or 32-bit default for a code segment is normally based on the following criteria:
•Performance — Always use 32-bit code segments when possible. They run much faster than 16-bit code segments on P6 family processors, and somewhat faster on earlier IA-32 processors.
•The operating system the code segment will be running on — If the operating system is a 16-bit operating system, it may not support 32-bit program modules.
•Mode of operation — If the code segment is being designed to run in real-address mode, virtual-8086 mode, or SMM, it must be a 16-bit code segment.
•Backward compatibility to earlier IA-32 processors — If a code segment must be able to run on an Intel 8086 or Intel 286 processor, it must be a 16-bit code segment.


The D flag in a code-segment descriptor determines the default operand-size and address-size for the instructions of a code segment. (In real-address mode and virtual-8086 mode, which do not use segment descriptors, the default is 16 bits.) A code segment with its D flag set is a 32-bit segment; a code segment with its D flag clear is a 16-bit segment.

Executable code segment. The flag is called the D flag and it indicates the default length for effective addresses and operands referenced by instructions in the segment. If the flag is set, 32-bit addresses and 32-bit or 8-bit operands are assumed; if it is clear, 16-bit addresses and 16-bit or 8-bit operands are assumed.
The instruction prefix 66H can be used to select an operand size other than the default, and the prefix 67H can be used select an address size other than the default.

The 32-bit operand prefix can be used in real-address mode programs to execute the 32-bit forms of instructions. This prefix also allows real-address mode programs to use the processor’s 32-bit general-purpose registers.
The 32-bit address prefix can be used in real-address mode programs, allowing 32-bit offsets.

The IA-32 processors beginning with the Intel386 processor can generate 32-bit offsets using an address override prefix; however, in real-address mode, the value of a 32-bit offset may not exceed FFFFH without causing an exception.

Assembler Usage:
If a code segment that is going to run in real-address mode is defined, it must be set to a USE 16 attribute. If a 32-bit operand is used in an instruction in this code segment (for example, MOV EAX, EBX), the assembler automatically generates an operand prefix for the instruction that forces the processor to execute a 32-bit operation, even though its default code-segment attribute is 16-bit.

The 32-bit operand prefix allows a real-address mode program to use the 32-bit general-purpose registers (EAX, EBX, ECX, EDX, ESP, EBP, ESI, and EDI).



When moving data in 32-bit mode between a segment register and a 32-bit general-purpose
register, the Pentium Pro processor does not require the use of a 16-bit operand size prefix;
however, some assemblers do require this prefix. The processor assumes that the 16 least-significant
bits of the general-purpose register are the destination or source operand. When moving a
value from a segment selector to a 32-bit register, the processor fills the two high-order bytes of
the register with zeros.

--------------------------------------------------

AMD:

3.3.2. 32-Bit vs. 16-Bit Address and Operand Sizes
The processor can be configured for 32-bit or 16-bit address and operand sizes. With 32-bit
address and operand sizes, the maximum linear address or segment offset is FFFFFFFFH
(232-1), and operand sizes are typically 8 bits or 32 bits. With 16-bit address and operand sizes,
the maximum linear address or segment offset is FFFFH (216-1), and operand sizes are typically
8 bits or 16 bits.
When using 32-bit addressing, a logical address (or far pointer) consists of a 16-bit segment
selector and a 32-bit offset; when using 16-bit addressing, it consists of a 16-bit segment selector
and a 16-bit offset.
Instruction prefixes allow temporary overrides of the default address and/or operand sizes from
within a program.
When operating in protected mode, the segment descriptor for the currently executing code
segment defines the default address and operand size. A segment descriptor is a system data
structure not normally visible to application code. Assembler directives allow the default
addressing and operand size to be chosen for a program. The assembler and other tools then set
up the segment descriptor for the code segment appropriately.
When operating in real-address mode, the default addressing and operand size is 16 bits. An
address-size override can be used in real-address mode to enable 32-bit addressing; however, the
maximum allowable 32-bit linear address is still 000FFFFFH (220-1).


3.6. OPERAND-SIZE AND ADDRESS-SIZE ATTRIBUTES
When the processor is executing in protected mode, every code segment has a default operandsize
attribute and address-size attribute. These attributes are selected with the D (default size)
flag in the segment descriptor for the code segment (see Chapter 3, Protected-Mode Memory
Management, in the Intel Architecture Software Developer’s Manual, Volume 3). When the D
flag is set, the 32-bit operand-size and address-size attributes are selected; when the flag is clear,
the 16-bit size attributes are selected. When the processor is executing in real-address mode,
virtual-8086 mode, or SMM, the default operand-size and address-size attributes are always 16
bits.
The operand-size attribute selects the sizes of operands that instructions operate on. When the
16-bit operand-size attribute is in force, operands can generally be either 8 bits or 16 bits, and
when the 32-bit operand-size attribute is in force, operands can generally be 8 bits or 32 bits.
The address-size attribute selects the sizes of addresses used to address memory: 16 bits or 32
bits. When the 16-bit address-size attribute is in force, segment offsets and displacements are 16
bits. This restriction limits the size of a segment that can be addressed to 64 KBytes. When the
32-bit address-size attribute is in force, segment offsets and displacements are 32 bits, allowing
segments of up to 4 GBytes to be addressed.
The default operand-size attribute and/or address-size attribute can be overridden for a particular
instruction by adding an operand-size and/or address-size prefix to an instruction (see
“Instruction Prefixes” in Chapter 2 of the Intel Architecture Software Developer’s Manual,
Volume 3). The effect of this prefix applies only to the instruction it is attached to.
Table 3-1 shows effective operand size and address size (when executing in protected mode)
depending on the settings of the D flag and the operand-size and address-size prefixes.

Dirk
Post 18 Jan 2013, 08:45
View user's profile Send private message Send e-mail Reply with quote
Cyril



Joined: 02 Jan 2013
Posts: 4
Location: Kryvyi Rih, Ukraine
Cyril
Thanks for the quick response. Yes, my bad, it was EDX, not EDI.

Well, I've made some changes - but still nothing.

Code:
use32
org 100h

        mov eax, 0000B101h      ; check for PCI BIOS presence
        sub edi, edi            ; why?
        int 1Ah                 ; time of the day AND PCI functions?
        cmp edx, 'PCI '         ; EDX has ' ICP'; FASM reverses it back
        je next                 ; success -> proceed
        stc                     ; set carry flag to 1 (=error)
        jmp error               ; error handling

next:
        mov ah, 9
        mov dx, successmsg      ; print success message
        int 21h
        int 20h

error:
        mov ah, 9
        mov dx, errormsg        ; print error message
        int 21h
        int 20h

successmsg      db      'PCI BIOS found!', 24h, 0Dh, 0Ah
errormsg        db      'PCI BIOS not found!', 24h, 0Dh, 0Ah
    


In fact, I'm not really sure it's because of the "use32" directive. Otherwise it would say something like "wrong opcode". But who knows?

Could you also explain what means "FASM reverses the string", from ICP back to PCI? When does that happen and why? No matter if I write ' ICP' or 'PCI ' in the listing, the result is total zero.

And also what's the difference between "call error" and "jump error"? I heard "call" changes some flags. (How) does it matter?

2freecrac: thanks a lot for the quote, but that's too much for me to grasp for now... Smile
Post 18 Jan 2013, 10:42
View user's profile Send private message Visit poster's website Reply with quote
Picnic



Joined: 05 May 2007
Posts: 1288
Location: Paradise Falls
Picnic
Cyril wrote:

Could you also explain what means "FASM reverses the string", from ICP back to PCI? When does that happen and why?

Hi,
See fasm's FAQ, third question.
Post 18 Jan 2013, 12:45
View user's profile Send private message Reply with quote
JohnFound



Joined: 16 Jun 2003
Posts: 3502
Location: Bulgaria
JohnFound
Remove this "use32".
Post 18 Jan 2013, 13:39
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
LocoDelAssembly
Your code has a bug


Joined: 06 May 2005
Posts: 4633
Location: Argentina
LocoDelAssembly
How could I missed that use32...

Code:
org 100h
;       INT 1A - PCI BIOS v2.0c+ - INSTALLATION CHECK
        mov ax, $B101
        xor edi, edi
        int $1A

;Return:
;AH = 00h if installed
;CF clear
;EDX = 20494350h (' ICP')
;EDI = physical address of protected-mode entry point (see #00731)
;AL = PCI hardware characteristics (see #00730)
;BH = PCI interface level major version (BCD)
;BL = PCI interface level minor version (BCD)
;CL = number of last PCI bus in system
;EAX, EBX, ECX, and EDX may be modified
;all other flags (except IF) may be modified

        jc  error

        cmp ah, 0
        jne error

        cmp edx, 'PCI '
        je  error

        mov dx, successmsg

print:
        mov ah, 9
        int 21h

        int 20h

error:
        mov dx, errormsg
        jmp print

successmsg      db      'PCI BIOS found!', 24h
errormsg        db      'PCI BIOS not found!', 24h    
This code reported error and success in DosBox 0.74 and Windows XP SP3 respectively.

And to clarify why use32 must not be used when the processor is running in 16-bit mode, this is the dissassembly produced with DEBUG (with some comments of mine):
Code:
B801B1        MOV     AX,B101
0000          ADD     [BX+SI],AL ; This is the highest two bytes of MOV EAX, 0000B101 misinterpreted as an instruction because of the missing operand size override prefix 
29FF          SUB     DI,DI
CD1A          INT     1A
81FF2049      CMP     DI,4920
; The next two instructions appear because of the same issue.
43            INC     BX ; 43h = 'C'
50            PUSH    AX ; 50h = 'P'
7406          JZ      0117
F9            STC
E80A00        CALL    011F
0000          ADD     [BX+SI],AL ; Same problem again
B409          MOV     AH,09
66            DB      66 ; Operand size override prefix. The next instruction is MOV EDX, ... actually.
BA2B01        MOV     DX,012B
CD21          INT     21 ; It won't be executed because the processor interpreted "MOV EDX, 21CD012Bh" and the highest two bytes of the operand are precisely this instruction
CD20          INT     20
B409          MOV     AH,09
66            DB      66
BA3B01        MOV     DX,013B
CD21          INT     21 ; Won't be executed because of the same reason as before.
CD20          INT     20    
Post 18 Jan 2013, 18:13
View user's profile Send private message Reply with quote
Cyril



Joined: 02 Jan 2013
Posts: 4
Location: Kryvyi Rih, Ukraine
Cyril
Thank you VERY much!

So it appears, "use32" was the real problem! In fact, I didn't know that I could use 32-bit registers in 16-bit mode and it would work.

As for your corrections. I have a few more questions. I've noticed that if I do only

Code:
org 100h

        mov ax, $B101 
        xor edi, edi 
        int 1Ah          ; this is more convenient for me to read Smile
        jc error
        
        mov dx, successmsg 

print: 
        mov ah, 9 
        int 21h 
        int 20h 

error: 
        mov dx, errormsg 
        jmp print 

successmsg      db      'PCI BIOS found!', 24h 
errormsg        db      'PCI BIOS not found!', 24h
    


and remove

Code:
cmp ah, 0 
jne error 

cmp edx, 'PCI ' 
je  error 
    


it works flawlessly. Why do I need to do triple check (jump if carry, AH not 0, 'PCI ' string in EDX) if the carry flag tells about success or error right away? Or is my variant too "dirty"?
Post 19 Jan 2013, 10:54
View user's profile Send private message Visit poster's website Reply with quote
shutdownall



Joined: 02 Apr 2010
Posts: 518
Location: Munich
shutdownall
You should check if AH is 0.
Please refer to the documentation.

Quote:

Return:
AH = 00h if installed
CF clear
EDX = 20494350h (' ICP')
EDI = physical address of protected-mode entry point (see #00731)
AL = PCI hardware characteristics (see #00730)
BH = PCI interface level major version (BCD)
BL = PCI interface level minor version (BCD)
CL = number of last PCI bus in system
EAX, EBX, ECX, and EDX may be modified
all other flags (except IF) may be modified


It says: CF is clear, it does not say CF is cleared when PCI BIOS is installed. I would assume that CF is cleared always as it is not pointed out. So I would check if AH is 0.

By the way, this check is more likely done with

Code:
or ah,ah
jnz error
    


This is done faster and better code.
The zero flag will be set on "or ah,ah" when ah is zero.
jnz = jump if not zero

Have fun.

PS: I think it would be hard to find a PC without PCI BIOS - so is check is more obsolete today ...
Post 19 Jan 2013, 11:56
View user's profile Send private message Send e-mail Reply with quote
ACP



Joined: 23 Sep 2006
Posts: 204
ACP
shutdownall wrote:

PS: I think it would be hard to find a PC without PCI BIOS - so is check is more obsolete today ...


I believe DosBox is one that return no PCI BIOS result in case of this call which would be hardly a surprise if you look at BIOS code mapped into memory - its more of a stub than true BIOS. So this might not be as obsolete as it might look like assuming DOS API is not obsolete either.

Also if this is supposed to be part of real application than first one must check if 32bit registers are supported by detecting cpu type.

The INT 1Ah requires also free space on stack - don't forget about it if you will convert from COM file format to EXE (add appropriate "stack [value]" keyword to the source).
Post 19 Jan 2013, 15:38
View user's profile Send private message Reply with quote
Cyril



Joined: 02 Jan 2013
Posts: 4
Location: Kryvyi Rih, Ukraine
Cyril
shutdownall wrote:
PS: I think it would be hard to find a PC without PCI BIOS - so this check is more obsolete today ...


In this case it says, "PCI BIOS not found!" Wink We must foresee many things while programming in asm, don't we? Smile

And yes, it's supposed to be a part of a bigger application - but it will be in COM format. I'm just starting to learn, I don't want to mess up with EXEs for now.
Post 19 Jan 2013, 16:13
View user's profile Send private message Visit poster's website Reply with quote
LocoDelAssembly
Your code has a bug


Joined: 06 May 2005
Posts: 4633
Location: Argentina
LocoDelAssembly
Still errors? Can't believe it... After changing JE with JNE it now says not found in both DosBox and WindowsXP, and in the case of WindowsXP under VirtualBox EDI=EDX=0.
Code:
AX=0001  BX=0000  CX=0055  DX=0000  SP=FFFE  BP=0000  SI=0000  DI=0000
DS=19A9  ES=19A9  SS=19A9  CS=19A9  IP=0112   NV UP EI PL ZR NA PE NC    
(I've checked EDX=EDI=0 using a different method to make sure I read the full 32-bit reg)
Post 19 Jan 2013, 17:51
View user's profile Send private message Reply with quote
freecrac



Joined: 19 Oct 2011
Posts: 117
Location: Germany Hamburg
freecrac
Cyril wrote:
In fact, I didn't know that I could use 32-bit registers in 16-bit mode and it would work.
If we want to use in the 16-bit mode 32 bit register an/or 32 bit operands(on 80386+), then we need an oprerandsize prefix and/or an adresssize prefix.
Without those prefixeses we can only use 16 bit operands and 16 bit adresses. (In the 16-bit mode it is also possible to use 64 Bit MMX- and 128 bit SSE- register, if the CPU provide these extensions(testing CPUID instruction).)

In the 32 bit mode the basic of those adresssize/operandsize prefixes will l be fundamentally changed. So if we want to use in the 32 bit mode 32 bit register and/or 32 bit operands, then we have to leave off those prefixes from our opcode.
But if we want to use in the 32 bit mode 16 bit operands and 16 bit addsesses, then we have to add those prefixes to our opcode.
The oprerandsize prefix and the adresssize prefix works diametrically opposite to the 16 bit mode for the real addressmode and for the protected mode.

..

So it is also possible with the 16 bit protected mode to use 32 bit address-register for to address the full address-space of the 32 bit area of 4 GB memmory. (With enlarging the default segmentsize from 64 KB in the GDT/LDT segment entries.)
Additional we can use all of our ram of 4 GB, if it psysical exist and if it free and not preallocated from the mainboard bios for peripherie components.

It is also possible to use the 32 bit mode with the real address mode, but most of the bios routines are build for the 16 bit mode and do not work together with the 32 bit mode and this exacctly is the reason why we have to use the 16 bit mode, if we want to use 16 bit software interrupts.

Dirk
Post 19 Jan 2013, 21:20
View user's profile Send private message Send e-mail Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2020, Tomasz Grysztar. Also on GitHub, YouTube, Twitter.

Website powered by rwasa.