3

I'm trying to add VESA video card detection to my system information program and can't seem to even put together code that works. I've looked at this thread: How to get VESA BIOS Information and this page on OSDev: https://wiki.osdev.org/VESA_Video_Modes which has code using __attribute__ ((packed)) written for the gcc compiler that's incompatible with my Digital Mars compiler.

All I really want is the VESA Version, OEMString, Total Memory, and if VESA 2.0 is supported, the OEMModel string, but if I have to process the entire ES:DI stack to get that information, so be it. However, that's where I'm stuck. I simply don't know how to grab that information and put it into a structure despite the example code given.

I know this site isn't for writing code for the questioner, but I'm hoping someone can help get me started so I can study working code and learn how to accomplish this. I don't care if it's in assembly or C++, though I have a tad more experience with C++.

I'm using the MARS C/C++ compiler. The generated programs will be 16-bit DOS programs.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Charlie Dobson
  • 193
  • 1
  • 11
  • What part of the code is incompatible? Why can you not switch compilers? – Taekahn May 19 '22 at 00:59
  • Tthe OSDev structure wants to use `__attribute__ ((packed))` which doesn't work in DMC, and it provides a function example of `v86_bios(0x10, {ax:0x4f00, es:SEG(vib), di:OFF(vib)}, &out);` but DMC does not have `v86_bios()`. I'm writing a suite of utilities and already have a few written DMC and wanted to keep everything uniform. I could switch compilers but then I'd have to re-write a bunch of code. For example, to create a TUI I use DMC's `disp_open()` function which comes with various support function like `disp_puts()` and `disp_printf()`. DMC also has easy DOS mouse support. – Charlie Dobson May 19 '22 at 01:09
  • 1
    Does Digital Mars not have its own syntax for packing structs without padding for alignment, if it doesn't support the GNU C attribute for it? – Peter Cordes May 19 '22 at 01:20
  • 3
    I had to look that one up in the documentation. It looks like I can probably use `#define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop))`. Then it looks like I can use `PACK( struct myStruct{ });` to accomplish that. I'd have to test that out. – Charlie Dobson May 19 '22 at 01:57
  • The code compiles with the above `#define` and `PACK` statements. I'll have to play around with some code, but if anyone has some additional thoughts or examples, please post them here. – Charlie Dobson May 19 '22 at 02:04
  • That should do it, then. You can look at the compiler-generated asm to make sure it's doing what you want, and using the right `sizeof(struct)`. – Peter Cordes May 19 '22 at 15:58
  • But what function do I use? Is `int86()` synonymous with `v86_bios()`? – Charlie Dobson May 19 '22 at 19:10
  • 1
    `v86_bios` isn't provided by any compiler I'm aware of but is a common name for a library function that switches to v8086 mode (with its own virtual monitor) performs a task with a given set of inputs and you can access the results in registers or memory. Usually such a monitor has to be written yourself. The other option is to switch back to real mode run real mode code to do the software interrupt you want and return back to protected mode. `int86` though is usually a 16-bit DOS function that runs in code compiled for 16-bit DOS envs (not 32-bit or 64-bit) and part of 16-bit DOS C libraries – Michael Petch May 19 '22 at 19:17
  • 2
    I guess the question is. Are you writing 32-bit code running in protected mode or 16-bit code? I gather from the tags it is 16-bit code. If that's the case you should be able to write code with just `int86` to make the 16-bit MS-DOS calls directly. `v86_bios` is a function in a set of libraries for code that was targeting 32-bit protected mode. If you are writing 16-bit DOS apps just develop the equivalent with `int86`. – Michael Petch May 19 '22 at 19:20
  • `int86` may be implemented differently with different parameters (but ultimately do similar things). One would have to look at your C library definition of `int86` to figure out how to pass data and parameters back and forth – Michael Petch May 19 '22 at 19:27
  • 1
    Thanks! I'm writing real mode x86 code. A lot of my system detection code is already using `int86()` so I should be able to get it to work. I'll test it out and confirm. – Charlie Dobson May 19 '22 at 19:50
  • Yeah if this is 16-bit code just use the `int86()` call. The OSDev wiki was developed assuming people were writing 32-bit code calling down to real mode interrupts either using a virtual v86 monitor or switching into real mode and then back. So they used library functions for doing that kind of thing using home grown utilities like a `v86_bios` function etc. In the 16-bit world such functions are just ways to do the kind of thing that `int86()` does. `int86()` or an equivalent is available in most C DOS libraries. – Michael Petch May 19 '22 at 19:52
  • So trying to use `int86( 0x10, { ax:0x4F01, es:SEG(vbi), di:OFF(vbi) }, &vbi );` complains that input was expected, suggesting `x86()` doesn't like using the bracketed parameters. It also expected to see a `_REG` structure too, and not my custom `struct{}` using the example from OSDev. – Charlie Dobson May 20 '22 at 01:03
  • You will have to modify the code to conform to the `int86()` convention for your C compiler and DOS library. You will have to review the manual for that function. Realistically though the code on the OSDev Wiki is based upon the underlying interrupt int 10h/AX=4f00h to get the VESA information which is described here: http://www.ctyme.com/intr/rb-0273.htm – Michael Petch May 20 '22 at 01:14
  • So if I do something like `int86( 0x10, &inregs, &outregs );`, how do I get everything from ES:DI and put it into the `struct{}`? – Charlie Dobson May 20 '22 at 01:53
  • 1
    If I'm looking at the docs correctly you would actually have to use `int86x` to access the segment registers, not `int86`. https://digitalmars.com/rtl/bios.html#_int86x – Michael Petch May 20 '22 at 02:12
  • 1
    Looks like I was able to accomplish this using `int86x()` and `FP_SEG()` & `FP_OFF()`. I'd post the full code but there isn't enough characters available to me in these boxes to write it all out. Initially I thought it wasn't working because under OEMString I was getting "761295520", but it turns out that string is used on early ATI video cards as stated in the link http://www.ctyme.com/intr/rb-0273.htm. Thanks to @MichaelPetch for the assistance! – Charlie Dobson May 23 '22 at 14:50

1 Answers1

3

I was able to get all related VESA information using the following code, no PACKing needed:

typedef struct _VBE_INFO
{
    char VbeSignature[4];
    uint16 VbeVersion;
    char FAR *fpOemString;
    uint32 Capabilities;
    uint16 FAR *fVideoMode;
    uint16 TotalMemory;
    /* VESA 2.x */
    uint16 OemSoftwareRev
    char FAR *fpOemVendorName;
    char FAR *fpOemProductName;
    char FAR *fpOemProductRev;
    char Reserved[222];
    char OemData[256];
} VBE_INFO;

VBE_INFO FAR *VbeInfo;
inregs.x.ax = 0x4F00;
sregs.es    = FP_SEG( VbeInfo );
inregs.x.di = FP_OFF( VbeInfo ):
int86x( 0x10, &inregs, &outregs, &sregs );

Then all your data is sitting in VbeInfo->? where ? is defined in the structure. E.g. VbeInfo->fpOemString contains the VESA v1.x Oem String data for the card. For VESA 2.x information, use the following code:

VBE_INFO FAR *VbeInfo;
_fstrncpy( VbeInfo->VbeSignature, "VBE2", 4 );
inregs.x.ax = 0x4F00;
sregs.es    = FP_SEG( VbeInfo );
inregs.x.di = FP_OFF( VbeInfo );
int86x( 0x10, &inregs, &outregs, &sregs );

Then information below the VESA 2.x comment in the struct will be populated.

Charlie Dobson
  • 193
  • 1
  • 11