VOGONS


Reply 20 of 24, by Myloch

User metadata
Rank Oldbie
Rank
Oldbie

English Pc globe 4.0 suffers this too.

"Gamer & collector for passion, I firmly believe in the preservation and the diffusion of old/rare software, against all personal egoisms"

Reply 21 of 24, by moog

User metadata
Rank Member
Rank
Member

I've made a little progress on tracking down why Stargunner has this problem.

Those values I posted before are realmode register dumps.

Interrupt divide by zero, stack:
2DB5 C000 3A07 000C 0000 0000 2CCB 0000
0000 28E2 0000 0000 12A2

It felt like chasing ghosts! The PMODE/W stub doesn't have a string like "Interrupt divide by zero, stack:", and Watcom (which it was compiled with) doesn't do that either, and the game's Linear Executable (which is not compressed btw!) also doesn't have this string. So where is this coming from? Not software, apparently.

Upon staring this code down with DEBUG from FreeDOS, I got a little better output, and in fact one layer deeper into the offending code:

Divide error
AX=0000 BX=00F0 CX=0150 DX=0100 SP=03E8 BP=0000 SI=000C DI=0000
DS=0000 ES=136D SS=227F CS=C000 IP=2DB5
OV UP EI PL NZ NA PE CY
C000:2DB5 F7F3 div bx

Segment 0xC000 is the VGA BIOS. So a quick glance at my VGA BIOS courtesy of IDA Free 9.2 and https://www.3dfxzone.it/dir/3dfx/utilities/?objid=558, we get the offending code:

seg000:2D8F ; =============== S U B R O U T I N E =======================================
seg000:2D8F
seg000:2D8F
seg000:2D8F sub_2D8F proc near ; CODE XREF: sub_2C9D:loc_2CC8↑p
seg000:2D8F ; sub_2C9D:loc_2CF9↑p
seg000:2D8F push bx
seg000:2D90 push dx
seg000:2D91 push si
seg000:2D92 call sub_2DE8
seg000:2D95 mov al, ds:byte_449
seg000:2D98 call sub_2DFE
seg000:2D9B mov bx, ax
seg000:2D9D mov al, ds:byte_449
seg000:2DA0 call sub_2BAB
seg000:2DA3 jmp cs:off_2DD6[si]
seg000:2DA8 ; ---------------------------------------------------------------------------
seg000:2DA8
seg000:2DA8 loc_2DA8: ; CODE XREF: sub_2D8F+14↑j
seg000:2DA8 ; DATA XREF: seg000:off_2DD6↓o ...
seg000:2DA8 div bx
seg000:2DAA imul ds:word_485
seg000:2DAE and al, 0F0h
seg000:2DB0 jmp short loc_2DCF
seg000:2DB2 ; ---------------------------------------------------------------------------
seg000:2DB2
seg000:2DB2 loc_2DB2: ; CODE XREF: sub_2D8F+14↑j
seg000:2DB2 ; DATA XREF: seg000:2DDC↓o ...
seg000:2DB2 shl bx, 2
seg000:2DB5
seg000:2DB5 loc_2DB5: ; CODE XREF: sub_2D8F+14↑j
seg000:2DB5 ; DATA XREF: seg000:2DDA↓o ...
seg000:2DB5 div bx
seg000:2DB7 and al, 0F0h
seg000:2DB9 jmp short loc_2DCF
seg000:2DBB ; ---------------------------------------------------------------------------
seg000:2DBB
seg000:2DBB loc_2DBB: ; CODE XREF: sub_2D8F+14↑j
seg000:2DBB ; DATA XREF: seg000:2DE6↓o
seg000:2DBB div bx
seg000:2DBD mov bx, 3
seg000:2DC0 cwd
seg000:2DC1 div bx
seg000:2DC3 imul ax, 3
seg000:2DC6
seg000:2DC6 loc_2DC6: ; CODE XREF: sub_2D8F+3E↓j
seg000:2DC6 test al, 7
seg000:2DC8 jz short loc_2DCF
seg000:2DCA sub ax, 3
seg000:2DCD jmp short loc_2DC6
seg000:2DCF ; ---------------------------------------------------------------------------
seg000:2DCF
seg000:2DCF loc_2DCF: ; CODE XREF: sub_2D8F+21↑j
seg000:2DCF ; sub_2D8F+2A↑j ...
seg000:2DCF call nullsub_4
seg000:2DD2 pop si
seg000:2DD3 pop dx
seg000:2DD4 pop bx
seg000:2DD5 retn
seg000:2DD5 sub_2D8F endp

Now what's interesting is that how the `div` works in realmode. Remember, these are our register values, I'll only put the ones that matter:

AX=0000 BX=00F0 DX=0100

`div` will divide a 32 bit integer made of DX and AX by the specified source register, in this case BX, and store the quotient in AX, and the remainder in DX. So we got this on our hands:

0x01000000 / 0x00F0

Or in other words

16,777,216 / 240

The result of this division as per the implementation of `div`, will make `0x11111` go to AX, and `0x10` go to DX. But, this is incredibly wrong because it overflows the register. So the real error is not necessarily a divide by zero, but an overflow as a result of division. So we're probably looking for `int 10h` calls that mess things up.

Going back to Stargunner, with this renewed intel we can correctly interpret the cryptic dump:

Interrupt divide by zero, stack:
AX=2DB5 BX=C000 CX=3A07 DX=000C SP=0000 BP=0000 SI=2CCB DI=0000
DS=0000 ES=28E2 SS=0000 CS=0000 IP=12A2

However, I did not find anything interesting in CS:IP 0x000012A2.

I'll post more, when I find it.

Last edited by moog on 2025-11-25, 18:13. Edited 2 times in total.

Audigy 2 ZS in FreeDOS
LinLin adapter documentation
+ various capacitor list threads

Reply 22 of 24, by Kahenraz

User metadata
Rank l33t
Rank
l33t

How did you get that visual debugger for a DOS executable?

Reply 23 of 24, by moog

User metadata
Rank Member
Rank
Member
Kahenraz wrote on 2025-11-25, 02:10:

How did you get that visual debugger for a DOS executable?

The screenshot you see is a disassembly of Voodoo 5500 VBIOS 1.06 that I linked, in IDA 9.2 Free for Linux.

To get a visual disassembly of a DOS executable, it depends what executable we're talking about. Stargunner 1.0b has 2 executables that exhibit exactly the same problem:
1. SETUP.EXE, 171613 bytes, sha256 b3137db05eae51cdfd3c16c19b2c5dc07c56eefebb7b4f0bb5004057176c69dc
2. STARGUN.EXE, 1066351 bytes, sha256 e0a61da0fd6c73f3305c3c02f1c7b27d1ea5f637f155a1373a1f2965564ef551

Both executables use the same PMODE/W v1.21 stub. You can replace the v1.21 stub with v1.33 stub, or replace it with a DOS32A 9.1.2 stub. All 3 stubs will run into the same problem while there are games that work fine with DOS32A on speedy CPUs, which indicates the problem is not caused by the stub.

In order to progress towards getting a visual disassembly on either of those files, you must strip the stub in order to obtain a Linear Executable. In both cases, the stub is 9808 bytes long, but the stripping process (done with PMWBIND.EXE or SB.EXE) will change 2 bytes early on in the Linear Executable.

Once you have obtained the Linear Executable, you have to analyze it with IDA 4.1, which has a free version available online: https://sourceforge.net/projects/hawk800/file … 41.zip/download

But IDA 4.1 is a TUI program, you probably say. Yes. But the database you can generate with IDA 4.1 is still readable with IDA 5.0, the first IDA to feature a GUI. Once the analysis is complete, save the database and open it with IDA 5.0.

If you skip IDA 4.1 and instead analyze the Linear Executable with IDA 5.0, you won't get the correct analysis. For comparison:
- IDA 5.0 LE analysis of STARGUN.EXE will yield 42 functions, because IDA 5.0 and onwards do not support Linear Executables at all
- IDA 4.1 LE analysis of STARGUN.EXE will yield 2492 functions

Audigy 2 ZS in FreeDOS
LinLin adapter documentation
+ various capacitor list threads

Reply 24 of 24, by Ringding

User metadata
Rank Member
Rank
Member

FWIW, the R6003 error is produced by Microsoft’s C runtime library.