VOGONS


First post, by superfury

User metadata
Rank l33t++
Rank
l33t++

My emulation uses an Adlib sound card (emulated). When I try to run the Monkey Island demo (midemo.exe) inside MS-DOS 6.22, I get the following error (with a very low res screen mode set, and an unresponsive command prompt (the monkey island demo is on emulated disk B (floppy #1 (0-based))):
"run-time error R6003
- integer divide by 0

B:\>"

Anyone knows why this is? Is this a bug in the demo, or is there something wrong with my Adlib emulation?

https://bitbucket.org/superfury/x86emu/src/4b … lib.c?at=master

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 2 of 17, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've already limited the CPU speed to ~330KIPS(8086 speed, essentially performing a delay of 4us every instruction executed). Is that correct?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 3 of 17, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie

It tries to measure how long a delay loop takes (or maybe how many loops a timer tick takes actually). It will later on use the calculated counts as delays for Adlib writes for example, so maybe these calculations fail somehow. Just debug where the div by zero happens and check it out.

Reply 4 of 17, by superfury

User metadata
Rank l33t++
Rank
l33t++

According to my debugger, it crashes at a division by 0 at 1C7E:2129. Do you know what it's doing at that point (running ms-dos 3.3)?

Registers at that point:
AX=1AA9
BX=FE0C
CX=0000
DX=2470
SP=84DA
BP=84E4
SI=0001
DI=494E
next instruction is at 1C7E:212B.
DS=249A
ES=F000
SS=26A9
FLAGS=F246
The instruction executed is opcode F6, full instruction is F6F3(DIV AX,BL). This is an divide of 0x1AFA(AX)/0xC.

The result is 23F, which doesn't fit in AH/AL. This causes an exception. What are these variables based on?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 5 of 17, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie

I recall it measures how many loops it does per timer tick and tries to divide that by 12 to use as loop counter later.

So your emulator executes too fast, or the simulated interrupts occur too slowly because the loop has counted so much.

Reply 6 of 17, by superfury

User metadata
Rank l33t++
Rank
l33t++

It runs when I increase the delay every instruction executed from 3us/instruction(CPU running at 330KIPS max) to 9us/instruction(CPU running at ~100KIPS max). This causes that counter not to overflow, but the game is very slow (running at at least 1/3 the speed it needs to run, adlib gives sound(although terrible, still to fix that problem), but about one note every 9 seconds as far as I can tell(during the game, when guybrush is standing at his initial location). As far as I can tell, the game should be made to play at normal speed (not so terribly slow that it messes up timing for the music). So the problem isn't that the emulator executes too fast, but that the IRQ is handled too slow?

Edit: Moved the IRQ to the CPU handling to increase the timing faster (as fast as the CPU can handle it while it's running). It doesn't even seem to use the IRQ0 to get that number: it's still at the value the BIOS sets it up: 18.2Hz. It doesn't read the timing registers either (have build some support for that too). So how does it know how much 'time' has passed according to it? Reading the adlib port? I've already implemented a delay of 4us in the adlib status port.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 7 of 17, by Harekiet

User metadata
Rank DOSBox Author
Rank
DOSBox Author

I'd imagine you just have some weird timing issues. Your avarage adlib detection routine would just start the adlib timer, wait a bit to see and then see if the timer bit changes in the adlib register and that's that. Dosbox is far from cycle exactly but most stuff worked fine.

Reply 8 of 17, by superfury

User metadata
Rank l33t++
Rank
l33t++

It still happens when the CPU is made to execute at a maximum of 0.33MIPS (the speed of a 8086 afaik). Both the adlib timer and IRQ0 timer are checked every instruction. The divide error still occurs. The adlib, CPU and IRQ0 timer are all updated based on a high resolution timer (either the Windows or the PSP one, SDL_getticks() if neither is supported). The CPU thread first updates the adlib timers, then the IRQ0 is checked, the instruction is executed and after that a delay of 3us is executed(using SDL_delay(0) until 3us have passed according to the high resolution timer). This makes sure the CPU never runs faster than 1/3MIPS(1000000us/3us=333333IPS, or 0.333333MIPS, which should be the speed of a 8086?

Last edited by superfury on 2015-08-22, 23:10. Edited 1 time in total.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 9 of 17, by superfury

User metadata
Rank l33t++
Rank
l33t++

Does the midemo.exe work in Dosbox? It seems to do something CPU timing-dependant which makes it crash with a divide by 0 in my emulator. I need to bring it down to ~100KIPS to prevent it from crashing. Adlib detection works fine both on 330KIPS and 100KIPS.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 10 of 17, by superfury

User metadata
Rank l33t++
Rank
l33t++

Just tested the midemo.exe in Dosbox: it runs. Both Monkey Island itself and the demo crash in my emulator with the same error now.

My CPU instruction routine is like this:
1. Tick keyboard input (1000x a second, using high res timer).
2. Tick adlib timers (using high res timer).
3. Tick IRQ0 timer (using high res timer).
4. Execute instruction.
5. Wait every 1000 instructions(unlimited speed, simply a delay of 0us(SDL_Delay)) or wait 3us (limited speed, which is used in this case, using the high resolution timers).

The high resolution timers work with QueryPerformanceCounter (Windows) or sceRtcGetCurrentTick(PSP) to check if enough time has passed to execute the timing. The current time is kept in microseconds.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 11 of 17, by superfury

User metadata
Rank l33t++
Rank
l33t++

I'm trying the demo of Body blows with my Adib emulation. It seems to actually use the Adlib, but it has too much noise it seems. Part of the sound (some tones) seems OK. Anyone knows what's going wrong here?

https://bitbucket.org/superfury/x86emu/src/fa … lib.c?at=master

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 12 of 17, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie

Regarding midemo,

No this is not a problem in Adlib detection or Adlib timers. It's about a calibration loop that increases a variable in a loop to measure time between timer interrupts.

There is only one place in midemo with "div bl" opcode, and the timer IRQ is set up about 15-20 opcodes before it, so it the timer value is not any more 18.2 Hz set by the BIOS.

I don't think you can say x86 speed in MIPS, as each instruction takes different amount of clock cycles. If you assume that, then you'll have a CPU that executes multiplications just as fast as simple register moves.

Reply 13 of 17, by superfury

User metadata
Rank l33t++
Rank
l33t++

According to my debugger, it doesn't change the IRQ0 speed (it's still at 18.2Hz at the point of the divide by 0). Is this a bug in my timer, or is it actually not updated from 18.2Hz(counter set to 0) at all? Btw, does monkey island update the entire timer value, or only the low 8-bits? My timer only updates when the entire 16-bits are written.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 14 of 17, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:

According to my debugger, it doesn't change the IRQ0 speed (it's still at 18.2Hz at the point of the divide by 0).

Well, I already said it does. There's only one place with "div bl" so according to your debugger you should see the outs to port 43h and 40h like 20 opcodes before "div bl".

superfury wrote:

Is this a bug in my timer, or is it actually not updated from 18.2Hz(counter set to 0) at all?

If you have to ask, I'd say it is a bug in your timer 😀 It is definitely set to something else than 0x0000 or 0xffff.

superfury wrote:

Btw, does monkey island update the entire timer value, or only the low 8-bits? My timer only updates when the entire 16-bits are written.

Entire timer. I bet it does not restart counting after the mode and new count is set, so the next timer interrupt happens much too late or something like that.
Time to see what 8254 datasheet says compared to your code.

Reply 15 of 17, by Harekiet

User metadata
Rank DOSBox Author
Rank
DOSBox Author

You'd use the emulated cpu or bus clock as the base of all your timing and timers if you hope to go for any kind of accuracy.
DOSBox just records an internal timestamp generated from basetime and the amount of clocks done in the current cpu time slice.
So save timestamp when adlib timer starts, then whenever they check the adlib status registers, generate new timestamp, check if the timer should have passed and update the register. No need to keep any timers just work with timestamps.

Reply 16 of 17, by superfury

User metadata
Rank l33t++
Rank
l33t++

There was indeed a bug in the PIT timer: It only supports command code 0x36, but the demo sets it to a different value, which causes the PIT to ignore the values written to port 0x40.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 17 of 17, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie

It writes 0xB6 which configures timer 2 mode, but after that it just loads value for timer 0.
So your emulator, does it support PC speaker at all then?

Could be a bug in executable, but in normal situations this will just work, for the following reasons:
-BIOS has already configured timer 0 mode to 0x36
-BIOS has already loaded timer 0 count
-BIOS or other software should not leave the timer 0 counter loading in weird state so it is expecting count
-so it should work without configuring timer 0 mode
-configuring timer 2 mode to 0xB6 without loading a count will just stop the timer from counting
-when game uses speaker, it only ever loads timer 2 count, never mode again
-even if game did not used speaker or load timer 2 count, the timer 2 is still expecting it.

So really, writing 0xB6 should not have affected loading of timer0 count, the channels are independent. Someone might first configure all channels, and then load counts for all timers, this is weird but not forbidden in any way.