VOGONS


I/O delay

Topic actions

First post, by Grunt

User metadata
Rank Newbie
Rank
Newbie

Hello guys,
I've been trying to slow down I/O (especially disk reading) so I can emulate real I/O responses from old computer in DosBox. I've notice that such a thing is alredy implemented and used in DosBox.

dos/dos.cpp:

71: #define DATA_TRANSFERS_TAKE_CYCLES 1
72: #ifdef DATA_TRANSFERS_TAKE_CYCLES
73:
74: #ifndef DOSBOX_CPU_H
75: #include "cpu.h"
76: #endif
77: static inline void modify_cycles(Bits value) {
78: if((4*value+5) < CPU_Cycles) {
79: CPU_Cycles -= 4*value;
80: CPU_IODelayRemoved += 4*value;
81: } else {
82: CPU_IODelayRemoved += CPU_Cycles/*-5*/; //don't want to mess with negative
83: CPU_Cycles = 5;
84: }
85:}

procedure modify_cycles() is then called from reading and writing into file:

644:	case 0x3f:		/* READ Read from file or device */
645: {
646: Bit16u toread=reg_cx;
647: dos.echo=true;
648: if (DOS_ReadFile(reg_bx,dos_copybuf,&toread)) {
649: MEM_BlockWrite(SegPhys(ds)+reg_dx,dos_copybuf,toread);
650: reg_ax=toread;
651: CALLBACK_SCF(false);
652: } else {
653: reg_ax=dos.errorcode;
654: CALLBACK_SCF(true);
655: }
656: modify_cycles(reg_ax);
657: dos.echo=false;
658: break;
659: }

Unfortunately, I can't figure out how it works. I/O read and write is definitley slower with modify_cycles (respectively disk I/O with DATA_TRANSFERS_TAKE_CYCLES 0 is fast as a lightning on modern PC) but it is still not enough. Even if I just try to leave CPU_Cycles = 5; there hardcoded.
I did found a way, which is SDL_Delay:

77: static inline void modify_cycles(Bits value) {
78: SDL_Delay(10);
79: if((10*value+5) < CPU_Cycles) {
80: CPU_Cycles -= 10*value;
81: ....

This works how anticipated. But it is not correct way how to delay execution. Entire DosBox app is delayed (including drawing functions, recording, etc.), not just execution of virtual machine.

So question follows: Which is proper way how to slow down or delay execution of core (VM?) in DosBox? Lets say in order hundreds of milliseconds or even whole second, not just a few cycles. Just so I can emulate seek times of old HDD or floppy disks.

Reply 1 of 4, by Qbix

User metadata
Rank DOSBox Author
Rank
DOSBox Author

Not a clear answer for you, as I have stumbled with it a few times a well.
But take a look on how BEEP is implemented or int14_wait.
That shows a way to pause the system without blocking the entire machine, but it is not really ideal.

Water flows down the stream
How to ask questions the smart way!

Reply 2 of 4, by Grunt

User metadata
Rank Newbie
Rank
Newbie

Ok, so answer is CALLBACK_Idle();. Now it is not exactly how do I remember it, it's even better:
file.php?id=65586&mode=view

It is perfect. Thanks a lot.

Attachments

  • doom.gif
    Filename
    doom.gif
    File size
    144.82 KiB
    Views
    886 views
    File license
    Fair use/fair dealing exception
Last edited by Grunt on 2019-07-13, 13:53. Edited 1 time in total.

Reply 4 of 4, by Grunt

User metadata
Rank Newbie
Rank
Newbie

Just a random number of calls for given cycle speed:

static inline void modify_cycles(Bits value) {
int i;


for (i=0;i<80;i++)
CALLBACK_Idle();
}
  • i<10 - too little
  • i<100 - too much
  • i between 40 and 80 - just fine for me.

Only as I said, some of the games uses either case 0x3f: /* READ Read from file or device */ or case 0x40: /* WRITE Write to file or device */ while writing text on screen or somehow else end up in modify_cycles() and they shouldn't. Vanilla DOOM for example uses direct writing on screen and works just fine (modify_cycle() is used only for real disk I/O - notice the flickering floppy disk):
file.php?mode=view&id=65655

There is no reason to require some milliseconds anyway. CALLBACK_Idle() stalls execution for pretty long to require accuracy in order of milliseconds.

Only except...are you sure CALLBACK_Idle is the right guy for the job?

void CALLBACK_Idle(void) {
[u]/* this makes the cpu execute instructions to handle irq's and then come back */[/u]
Bitu oldIF=GETFLAG(IF);
SETFLAGBIT(IF,true);
Bit16u oldcs=SegValue(cs);
Bit32u oldeip=reg_eip;
SegSet16(cs,CB_SEG);
reg_eip=CB_SOFFSET+call_idle*CB_SIZE;
DOSBOX_RunMachine();
reg_eip=oldeip;
SegSet16(cs,oldcs);
SETFLAGBIT(IF,oldIF);
if (!CPU_CycleAutoAdjust && CPU_Cycles>0)
CPU_Cycles=0;
}

I'm not sure.

Attachments

  • output.gif
    Filename
    output.gif
    File size
    1.77 MiB
    Views
    794 views
    File license
    Fair use/fair dealing exception