Discussion:
checking int2d amis appstring with djgpp
(too old to reply)
Ozkan Sezer (sezeroz@gmail.com) [via djgpp@delorie.com]
2024-02-23 21:11:46 UTC
Permalink
Hi everyone:

Does the following DJGPP port of SBEMU detection look correct?
It is an adaptation from a WatcomC (32 bit flat) code in here:
https://github.com/wbcbz7/sndlib-watcom/blob/master/sndlib.cpp#L41-L71

(I'm fairly rusty in gcc inline asm and djgpp address mapping, etc,
thanks in advance.)

int sndlib_sbemu_detect(void)
{
__dpmi_raddr addr;
uint32_t r_addr;
char* appstring;
int mx;

/* check for INT2D vector == NULL */
__dpmi_get_real_mode_interrupt_vector(0x2D, &addr);
r_addr = ((uint32_t)addr.segment<<4) + (uint32_t)addr.offset16;
if (!r_addr) return -1;

/* scan all multiplexes of INT 2D */
for (mx = 0; mx < 256; mx++)
{
__asm__ __volatile__ ("movb %0,%%ah"::"m"(mx));
__asm__ __volatile__ (
"xorb %al, %al\n"
"int $0x2d\n"
"cmpb $0xFF, %al\n" /* is this a free multiplex? */
"jz _found\n"
"xorw %dx, %dx\n" /* it is, return NULL pointer */
"xorw %di, %di\n"
"_found:\n");
__asm__ __volatile__ ("movw %%dx,%0":"=m"(addr.segment));
__asm__ __volatile__ ("movw %%di,%0":"=m"(addr.offset16));

/* check for SBEMU application string */
r_addr = ((uint32_t)addr.segment<<4) + (uint32_t)addr.offset16;
if (!r_addr) continue;
appstring = (char *)real2ptr(r_addr);
if (memcmp(appstring + 8,"SBEMU",5) == 0)
return mx;
}

return -1;
}


P.S.: real2ptr() in there actually returns
(void *) (real += __djgpp_conventional_base)
after a successful __djgpp_nearptr_enable()

--
O.S.
J.W. Jagersma (jwjagersma@gmail.com) [via djgpp@delorie.com]
2024-02-23 21:48:47 UTC
Permalink
Hi!
Post by Ozkan Sezer (***@gmail.com) [via ***@delorie.com]
Does the following DJGPP port of SBEMU detection look correct?
https://github.com/wbcbz7/sndlib-watcom/blob/master/sndlib.cpp#L41-L71
(I'm fairly rusty in gcc inline asm and djgpp address mapping, etc,
thanks in advance.)
int sndlib_sbemu_detect(void)
{
__dpmi_raddr addr;
uint32_t r_addr;
char* appstring;
int mx;
/* check for INT2D vector == NULL */
__dpmi_get_real_mode_interrupt_vector(0x2D, &addr);
r_addr = ((uint32_t)addr.segment<<4) + (uint32_t)addr.offset16;
if (!r_addr) return -1;
/* scan all multiplexes of INT 2D */
for (mx = 0; mx < 256; mx++)
{
__asm__ __volatile__ ("movb %0,%%ah"::"m"(mx));
__asm__ __volatile__ (
"xorb %al, %al\n"
"int $0x2d\n"
"cmpb $0xFF, %al\n" /* is this a free multiplex? */
"jz _found\n"
"xorw %dx, %dx\n" /* it is, return NULL pointer */
"xorw %di, %di\n"
"_found:\n");
__asm__ __volatile__ ("movw %%dx,%0":"=m"(addr.segment));
__asm__ __volatile__ ("movw %%di,%0":"=m"(addr.offset16));
This doesn't look right - you can't assume gcc preserves registers
between asm blocks.

But this is a real-mode interrupt, no? So you would have to do it via
__dpmi_int() here anyway, no asm involved.
Post by Ozkan Sezer (***@gmail.com) [via ***@delorie.com]
/* check for SBEMU application string */
r_addr = ((uint32_t)addr.segment<<4) + (uint32_t)addr.offset16;
if (!r_addr) continue;
appstring = (char *)real2ptr(r_addr);
if (memcmp(appstring + 8,"SBEMU",5) == 0)
return mx;
}
return -1;
}
P.S.: real2ptr() in there actually returns
(void *) (real += __djgpp_conventional_base)
after a successful __djgpp_nearptr_enable()
--
O.S.
Ozkan Sezer (sezeroz@gmail.com) [via djgpp@delorie.com]
2024-02-24 00:08:59 UTC
Permalink
On Sat, Feb 24, 2024 at 12:49 AM J.W. Jagersma (***@gmail.com)
[via ***@delorie.com] <***@delorie.com> wrote:
[...]
Post by J.W. Jagersma (***@gmail.com) [via ***@delorie.com]
Post by Ozkan Sezer (***@gmail.com) [via ***@delorie.com]
__asm__ __volatile__ ("movw %%dx,%0":"=m"(addr.segment));
__asm__ __volatile__ ("movw %%di,%0":"=m"(addr.offset16));
This doesn't look right - you can't assume gcc preserves registers
between asm blocks.
OK, should have been like this then, yes?

__asm__ __volatile__ (
"movb %2, %%ah\n"
"xorb %%al, %%al\n"
"int $0x2D\n"
"cmpb $0xFF,%%al\n" /* is this a free multiplex? */
"jz 1f\n"
"xorw %%dx, %%dx\n" /* it is, return NULL pointer */
"xorw %%di, %%di\n"
"1:\n"
"movw %%dx, %0\n"
"movw %%di, %1\n"
: "=m"(addr.segment),
"=m"(addr.offset16)
: "m"(mx) );
Post by J.W. Jagersma (***@gmail.com) [via ***@delorie.com]
But this is a real-mode interrupt, no? So you would have to do it via
__dpmi_int() here anyway, no asm involved.
Is the following correct ??

int sndlib_sbemu_detect(void)
{
__dpmi_raddr addr;
uint32_t r_addr;
__dpmi_regs regs;
char* appstring;
int mx;

/* check for INT2D vector == NULL */
__dpmi_get_real_mode_interrupt_vector(0x2D, &addr);
r_addr = ((uint32_t)addr.segment<<4) + (uint32_t)addr.offset16;
if (!r_addr) return -1;

/* scan all multiplexes of INT 2D */
for (mx = 0; mx < 256; mx++)
{
memset(&regs, 0, sizeof(regs));
regs.h.ah = mx;
__dpmi_int(0x2D, &regs);
if (regs.h.al != 0xFF)
continue;

/* check for SBEMU application string */
r_addr = ((uint32_t)regs.x.dx<<4) + (uint32_t)regs.x.di;
if (!r_addr) continue;
appstring = (char *)real2ptr(r_addr);
if (memcmp(appstring + 8,"SBEMU",5) == 0)
return mx;
}

return -1;
}

--
O.S.
Eli Zaretskii (eliz@gnu.org) [via djgpp@delorie.com]
2024-02-24 07:02:23 UTC
Permalink
Date: Sat, 24 Feb 2024 03:08:59 +0300
[...]
Post by J.W. Jagersma (***@gmail.com) [via ***@delorie.com]
Post by Ozkan Sezer (***@gmail.com) [via ***@delorie.com]
__asm__ __volatile__ ("movw %%dx,%0":"=m"(addr.segment));
__asm__ __volatile__ ("movw %%di,%0":"=m"(addr.offset16));
This doesn't look right - you can't assume gcc preserves registers
between asm blocks.
OK, should have been like this then, yes?
It is better to avoid inline assembly at all. I'm guessing Watcom did
that for speed or something, but does speed really matter here?
Is the following correct ??
int sndlib_sbemu_detect(void)
{
__dpmi_raddr addr;
uint32_t r_addr;
__dpmi_regs regs;
char* appstring;
int mx;
/* check for INT2D vector == NULL */
__dpmi_get_real_mode_interrupt_vector(0x2D, &addr);
r_addr = ((uint32_t)addr.segment<<4) + (uint32_t)addr.offset16;
if (!r_addr) return -1;
Instead of the above bit juggling to get r_addr, you could simply test
both segment and offset to be zero. After all, how else you'd get
zero in the linear address?
/* scan all multiplexes of INT 2D */
for (mx = 0; mx < 256; mx++)
{
memset(&regs, 0, sizeof(regs));
regs.h.ah = mx;
__dpmi_int(0x2D, &regs);
if (regs.h.al != 0xFF)
continue;
/* check for SBEMU application string */
r_addr = ((uint32_t)regs.x.dx<<4) + (uint32_t)regs.x.di;
if (!r_addr) continue;
appstring = (char *)real2ptr(r_addr);
if (memcmp(appstring + 8,"SBEMU",5) == 0)
return mx;
You cannot access conventional (below 1MB) memory like that. You need
to use one of the techniques described in the node "Xfer" in the
DJGPPFAQ Info manual, to fetch the 5 bytes to protected-mode memory
first. In your case, _dosmemget seems to be the best method.
Ozkan Sezer (sezeroz@gmail.com) [via djgpp@delorie.com]
2024-02-24 08:56:28 UTC
Permalink
Hello Eli,
Post by Eli Zaretskii (***@gnu.org) [via ***@delorie.com]
It is better to avoid inline assembly at all. I'm guessing Watcom did
that for speed or something, but does speed really matter here?
Most certainly no, speed is not a concern here. It's my ignorance in
attempting to translate watcom code
Post by Eli Zaretskii (***@gnu.org) [via ***@delorie.com]
Post by Ozkan Sezer (***@gmail.com) [via ***@delorie.com]
r_addr = ((uint32_t)addr.segment<<4) + (uint32_t)addr.offset16;
if (!r_addr) return -1;
Instead of the above bit juggling to get r_addr, you could simply test
both segment and offset to be zero. After all, how else you'd get
zero in the linear address?
Indeed
Post by Eli Zaretskii (***@gnu.org) [via ***@delorie.com]
Post by Ozkan Sezer (***@gmail.com) [via ***@delorie.com]
appstring = (char *)real2ptr(r_addr);
[...]
Post by Eli Zaretskii (***@gnu.org) [via ***@delorie.com]
You cannot access conventional (below 1MB) memory like that. You need
to use one of the techniques described in the node "Xfer" in the
DJGPPFAQ Info manual, to fetch the 5 bytes to protected-mode memory
first. In your case, _dosmemget seems to be the best method.
Thanks. So: is the following correct? Does it match the intention at
https://github.com/wbcbz7/sndlib-watcom/blob/master/sndlib.cpp#L41-L71

int sndlib_sbemu_detect(void)
{
__dpmi_raddr addr;
__dpmi_regs regs;
uint32_t r_addr;
char appstring[16];
int mx;

/* check for INT2D vector == NULL */
__dpmi_get_real_mode_interrupt_vector(0x2D, &addr);
if (!addr.segment || !addr.offset16) return -1;

/* scan all multiplexes of INT 2D */
for (mx = 0; mx < 256; mx++)
{
memset(&regs, 0, sizeof(regs));
regs.h.ah = mx;
__dpmi_int(0x2D, &regs);
if (regs.h.al != 0xFF)
continue;

/* check for SBEMU application string */
r_addr = ((uint32_t)regs.x.dx<<4) + (uint32_t)regs.x.di;
if (!r_addr) continue;
dosmemget(r_addr, 16, appstring);
if (memcmp(appstring + 8,"SBEMU",5) == 0)
return mx;
}

return -1;
}
Eli Zaretskii (eliz@gnu.org) [via djgpp@delorie.com]
2024-02-24 10:08:05 UTC
Permalink
Date: Sat, 24 Feb 2024 11:56:28 +0300
Post by Eli Zaretskii (***@gnu.org) [via ***@delorie.com]
Post by Ozkan Sezer (***@gmail.com) [via ***@delorie.com]
appstring = (char *)real2ptr(r_addr);
[...]
Post by Eli Zaretskii (***@gnu.org) [via ***@delorie.com]
You cannot access conventional (below 1MB) memory like that. You need
to use one of the techniques described in the node "Xfer" in the
DJGPPFAQ Info manual, to fetch the 5 bytes to protected-mode memory
first. In your case, _dosmemget seems to be the best method.
Thanks. So: is the following correct? Does it match the intention at
https://github.com/wbcbz7/sndlib-watcom/blob/master/sndlib.cpp#L41-L71
LGTM, but of course it needs to be tested ;-)
Ozkan Sezer (sezeroz@gmail.com) [via djgpp@delorie.com]
2024-02-24 10:11:57 UTC
Permalink
Post by Eli Zaretskii (***@gnu.org) [via ***@delorie.com]
Post by Ozkan Sezer (***@gmail.com) [via ***@delorie.com]
Thanks. So: is the following correct? Does it match the intention at
https://github.com/wbcbz7/sndlib-watcom/blob/master/sndlib.cpp#L41-L71
LGTM, but of course it needs to be tested ;-)
Thanks!

And yes, will be testing on real hw soon.
Ozkan Sezer (sezeroz@gmail.com) [via djgpp@delorie.com]
2024-02-25 12:03:42 UTC
Permalink
Post by Ozkan Sezer (***@gmail.com) [via ***@delorie.com]
Post by Eli Zaretskii (***@gnu.org) [via ***@delorie.com]
Post by Ozkan Sezer (***@gmail.com) [via ***@delorie.com]
Thanks. So: is the following correct? Does it match the intention at
https://github.com/wbcbz7/sndlib-watcom/blob/master/sndlib.cpp#L41-L71
LGTM, but of course it needs to be tested ;-)
Thanks!
And yes, will be testing on real hw soon.
Tested and working good. Thanks to all who helped.

J.W. Jagersma (jwjagersma@gmail.com) [via djgpp@delorie.com]
2024-02-24 11:05:38 UTC
Permalink
Post by Ozkan Sezer (***@gmail.com) [via ***@delorie.com]
[...]
Post by J.W. Jagersma (***@gmail.com) [via ***@delorie.com]
Post by Ozkan Sezer (***@gmail.com) [via ***@delorie.com]
__asm__ __volatile__ ("movw %%dx,%0":"=m"(addr.segment));
__asm__ __volatile__ ("movw %%di,%0":"=m"(addr.offset16));
This doesn't look right - you can't assume gcc preserves registers
between asm blocks.
OK, should have been like this then, yes?
__asm__ __volatile__ (
"movb %2, %%ah\n"
"xorb %%al, %%al\n"
"int $0x2D\n"
"cmpb $0xFF,%%al\n" /* is this a free multiplex? */
"jz 1f\n"
"xorw %%dx, %%dx\n" /* it is, return NULL pointer */
"xorw %%di, %%di\n"
"1:\n"
"movw %%dx, %0\n"
"movw %%di, %1\n"
: "=m"(addr.segment),
"=m"(addr.offset16)
: "m"(mx) );
Almost. You would also need to list the registers you modified,
otherwise gcc will make the wrong assumptions about their contents. So:

/* ... */
: "=m"(addr.segment),
"=m"(addr.offset16)
: "m"(mx)
: "ax", "dx", "di");

Although if I were to call a protected-mode interrupt, I would do the
control flow in C, so the asm can be simplified to:

unsigned ax = mx << 8;
__dpmi_raddr addr;

asm
(
"int $0x2D"
: "+a" (ax), /* ax */
"=d" (addr.segment), /* dx */
"=D" (addr.offset16) /* di */
);

if ((ax & 0xff) != 0xff)
continue;

/* ... */

But this is not very relevant to your question anymore, of course :)

Last version of the code you posted looks good to me, I would expect
that to work.
Ozkan Sezer (sezeroz@gmail.com) [via djgpp@delorie.com]
2024-02-24 11:20:02 UTC
Permalink
Post by J.W. Jagersma (***@gmail.com) [via ***@delorie.com]
Post by Ozkan Sezer (***@gmail.com) [via ***@delorie.com]
Post by J.W. Jagersma (***@gmail.com) [via ***@delorie.com]
Post by Ozkan Sezer (***@gmail.com) [via ***@delorie.com]
__asm__ __volatile__ ("movw %%dx,%0":"=m"(addr.segment));
__asm__ __volatile__ ("movw %%di,%0":"=m"(addr.offset16));
This doesn't look right - you can't assume gcc preserves registers
between asm blocks.
OK, should have been like this then, yes?
__asm__ __volatile__ (
"movb %2, %%ah\n"
"xorb %%al, %%al\n"
"int $0x2D\n"
"cmpb $0xFF,%%al\n" /* is this a free multiplex? */
"jz 1f\n"
"xorw %%dx, %%dx\n" /* it is, return NULL pointer */
"xorw %%di, %%di\n"
"1:\n"
"movw %%dx, %0\n"
"movw %%di, %1\n"
: "=m"(addr.segment),
"=m"(addr.offset16)
: "m"(mx) );
Almost. You would also need to list the registers you modified,
/* ... */
: "=m"(addr.segment),
"=m"(addr.offset16)
: "m"(mx)
: "ax", "dx", "di");
Although if I were to call a protected-mode interrupt, I would do the
unsigned ax = mx << 8;
__dpmi_raddr addr;
asm
(
"int $0x2D"
: "+a" (ax), /* ax */
"=d" (addr.segment), /* dx */
"=D" (addr.offset16) /* di */
);
if ((ax & 0xff) != 0xff)
continue;
/* ... */
But this is not very relevant to your question anymore, of course :)
Last version of the code you posted looks good to me, I would expect
that to work.
Thanks!
Loading...