Back to home page

DOS ain't dead

Forum index page

Log in | Register

Back to index page
Thread view  Board view
myrkraverk

09.12.2012, 23:41
 

Waiting for 3 (or 3.3) microseconds? 286 and up. (Developers)

Hi all,

Context. I've been playing with Sound Blaster coding, so far in DOSBox. The initialization routine is supposed to wait for 3 or 3.3 microseconds between the first two writes for the actual hardware. The docs I've found don't seem to agree on which. And at this point, it probably doesn't matter.

What is a good way to guarantee a wait for a few microseconds? Can I use the same code on a 286 while still guaranteeing long enough delay on a 64bit cpu who happens to be talking to a real sound blaster in an ISA slot? Those motherboards exists [even though I don't have one, nor a real sound blaster].

So far, I've just used interrupt 0x15 with ah=0x86 for a 1000 microsecond delay; which is long enough though quite excessive.

I believe I'm looking at the official documents, where they use a delay loop of "dec al; jnz delay" where al has been initialized to 0. Hence looping 255 times. I'm not sure that method is "future proof" or even sufficient with a modern CPU.

Also related. The Sound Blaster is said to take 100 microseconds to initialize itself. What steps can I take to wait just that long until there is 0xaa from the status port of the card? Again, those steps should ideally work on both real 286 and a modern CPU.

RayeR

Homepage

CZ,
10.12.2012, 03:09

@ myrkraverk
 

Waiting for 3 (or 3.3) microseconds? 286 and up.

Sorry I don't know SB programming but if there is available some status register that changes a value when device is ready it's the best way to read this status in a loop (you may add some n-cycles timeout to not hang the PC if there's is some error). Otherwise I would rather wait 1ms (it's only at once on system startup so nobody would notice such short delay) than use fixed loop counter that is dependent on CPU speed - it's a bad code. I think it's acceptable to use some counter based on number of IO reads because ISA (LPC) bus has comparable speed on 286 and modern PC. But CPU cycle was shortened by many magnitudes...

---
DOS gives me freedom to unlimited HW access.

Japheth

Homepage

Germany (South),
10.12.2012, 11:42

@ myrkraverk
 

Waiting for 3 (or 3.3) microseconds? 286 and up.

> What is a good way to guarantee a wait for a few microseconds? Can I use
> the same code on a 286 while still guaranteeing long enough delay on a
> 64bit cpu who happens to be talking to a real sound blaster in an ISA slot?
> Those motherboards exists [even though I don't have one, nor a real sound
> blaster].

One may query Keyboard controller port B ( cpu port 61h ). Bit 4 ("refresh request") toggles at a frequency of 1.193 MHz. It's often used for PC-speaker sound timings.

---
MS-DOS forever!

RayeR

Homepage

CZ,
10.12.2012, 18:04

@ Japheth
 

Waiting for 3 (or 3.3) microseconds? 286 and up.

> One may query Keyboard controller port B ( cpu port 61h ). Bit 4 ("refresh
> request") toggles at a frequency of 1.193 MHz. It's often used for
> PC-speaker sound timings.

Oh yes, I forgot, DJGPP use this as clock source for uclock() and udelay() precise timing...

---
DOS gives me freedom to unlimited HW access.

bretjohn

Homepage E-mail

Rio Rancho, NM,
10.12.2012, 15:52

@ myrkraverk
 

Waiting for 3 (or 3.3) microseconds? 286 and up.

Here's some code I've used in a lot of my programs to accomplish this task. It's written for A86, so you will need to change it a little depending on your assembler. You could optimize it some more if you wanted to, but it works OK just like it is.


;-----------------------------------------------------------------------------
;DELAY FOR A GIVEN NUMBER OF MICROSECONDS
;Inputs:  AX = number of microseconds to wait (0-65535)
;Outputs:
;Changes:
;   NOTE: Delays will be slightly longer than what's asked for, never shorter.
;         Accuracy decreases with small delays or slow computers!
;-----------------------------------------------------------------------------
DelayUSAX:
  PUSH AX,BX           ;Save used registers
  OR   AX,AX           ;Is there anything to do?
  JZ  >D90             ;If not, just quit
  MOV  BX,AX           ;Put it in BX
  MOV  AX,(0FFFFh/3)+1 ;Maximum time for each sub-delay
D10:                   ;Loop to here for each sub-delay
  CMP  BX,AX           ;Is it more than the maximum?
  JA  >D40             ;If so, just do the sub-delay
  MOV  AX,BX           ;If not, just do the remainder that's left
D40:                   ;Do the delay
  CALL DoDelayUS       ;Do the US Delay
  SUB  BX,AX           ;Subtract out how long we just waited
  JNZ  D10             ;If not 0 yet, do the sub-delay again
D90:                   ;We're done
  POP  BX,AX           ;Restore used registers
  RET

DoDelayUS:
  PUSH AX,BX,CX,DX ;Save used registers
  PUSHF            ;Save Flags
  SHL  AX,1        ;Multiply by 2
  MOV  BX,11932    ;Compensate for the fact
  MUL  BX          ;  that the clock frequency
  MOV  BX,10000    ;  is actually
  DIV  BX          ;  1.19318 MHz
  MOV  DX,AX       ;Save it
  CLI              ;Disable interrupts
  CALL GetTimer    ;Get the starting timer tick counter
  MOV  CX,AX       ;Save it
D20:               ;Keep looping to here until we've waited long enough
  MOV  BX,CX       ;Get the starting timer tick counter
  CALL GetTimer    ;Get the current timer tick counter
  SUB  BX,AX       ;Calculate the elapsed time
  CMP  BX,DX       ;Has it been long enough?
  JB   D20         ;If not, keep waiting
D90:               ;We're done
  POPF             ;Restore Flags
  POP  DX,CX,BX,AX ;Restore used registers
  RET

;-----------------------------------------------------------------------------
;GET CURRENT TIMER COUNTER FROM PORT 40h (THE CLOCK INTERRUPT)
;Inputs:
;Outputs: AX = Current timer counter word
;Changes:
;-----------------------------------------------------------------------------
GetTimer:
  PUSHF       ;Save flags
  CLI         ;DIsable interrupts
  MOV  AL,06h ;Bits 7:6 =  00 = Timer 0
              ;Bits 5:4 =  00 = Latch Counter
              ;Bits 3:1 = 011 = Mode 3 (Square Wave)
              ;Bit    0 =   0 = Binary Counter (16 bits)
  OUT  43h,AL ;Tell the PIT what wer'e going to do
  IN   AL,40h ;Get LSB of timer counter
  MOV  AH,AL  ;Save it
  IN   AL,40h ;Get MSB of timer counter
  XCHG AH,AL  ;Put things in the right order
  POPF        ;Restore flags
  RET

myrkraverk

10.12.2012, 21:22

@ bretjohn
 

Waiting for 3 (or 3.3) microseconds? 286 and up.

> Here's some code I've used in a lot of my programs to accomplish this task.

Thank you. You use the CLI instruction but never, STI. This confused me until I noticed PUSHF and POPF. Just pointing this out, in case others get confused too :-)

myrkraverk

10.12.2012, 21:16

@ myrkraverk
 

Waiting for 3 (or 3.3) microseconds? 286 and up.

Here is an email I received with CC Rugxulo.

> You ask on BTTR how to wait for a tiny amount of time:
> Doing a loop is a bad idea as modern CPU get faster and
> faster, so your delay gets too short or you have to use
> at least 32 bit counting and have to determine a "good"
> number of repetitions somehow, eg. by counting how many
> repetitions you can do in 1 second. Which is a classic
> reason for old software to overflow on new CPU, because
> you may need even 64 bit for that.
>
> I believe a classic method would be to do something as
> "in al,0x80" (Rugxulo? What is a good port for this?)
> based on the assumption that I/O for ISA-ish ports is
> taking a relatively constant amount of time even when
> the CPU is fast. You then do that as part of you loop
> and voila 255 repetitions are more than enough again.
>
> Also there are indeed int 15 delay functions, but I am
> not sure how well they work for tiny delays. I believe
> they use the 18.2 Hz timer for long delays? And may use
> the 1.19 MHz clock driving that for shorter delays in a
> polling loop style? Or maybe they use the RTC 32768 Hz
> clock for those? Or some sort of calibrated-number-of-
> repetitions style looping, with or without port read as
> delay activity? Again maybe Rugxulo knows a bit more :-)
>
> Regards, Eric
>
> PS: AMD now has a CPU with 8 cores a 4 GHz which claims
> to need only 125 Watt style cooling. Can loop a lot :-D

Rugxulo

Homepage

Usono,
10.12.2012, 22:03

@ myrkraverk
 

Waiting for 3 (or 3.3) microseconds? 286 and up.

Hi, (not sure why Eric expects me to know anything, I'm quite far from expert)

> Context. I've been playing with Sound Blaster coding, so far in DOSBox.

Is this meant to be widely deployed? I mean, are there real machines that will be running this? SoundBlaster is quite rare on actual hardware these days except on old machines.

> The initialization routine is supposed to wait for 3 or 3.3 microseconds
> between the first two writes for the actual hardware.
>
> What is a good way to guarantee a wait for a few microseconds? Can I use
> the same code on a 286 while still guaranteeing long enough delay on a
> 64bit cpu who happens to be talking to a real sound blaster in an ISA slot?
> Those motherboards exists [even though I don't have one, nor a real sound
> blaster].

I think SoundBlaster is dead, I don't think (m)any people will use it. Sure, it's well-documented, even fun (I guess, never tried) to program for it, but it's fairly useless. Not trying to discourage you, just being realistic. :-/

> So far, I've just used interrupt 0x15 with ah=0x86 for a 1000 microsecond
> delay; which is long enough though quite excessive.

I'm still not sure if you intend this to work on real hardware (actual 286?) or just anything you can find (DOSBox). For instance, I don't think WinXP supports int 15h, 86h very well. And of course DOSBox isn't accurate timings at all. I think every instruction in DOSBox takes only a single (virtual) clock cycle. So it's not reliable for timings (and only simulates a fast 486 or slow 586, at best).

> I believe I'm looking at the official documents, where they use a delay
> loop of "dec al; jnz delay" where al has been initialized to 0. Hence
> looping 255 times. I'm not sure that method is "future proof" or even
> sufficient with a modern CPU.

Older in-order cpus (e.g. 486) were less sensitive to wonky timing variations since they didn't have as aggressive power management and were more commonly used in single-tasking DOS days. You could 99% reliably check the timings of their x86 instructions from documents, unlike today.

> Also related. The Sound Blaster is said to take 100 microseconds to
> initialize itself. What steps can I take to wait just that long until
> there is 0xaa from the status port of the card? Again, those steps should
> ideally work on both real 286 and a modern CPU.

Do you have a real 286 for testing? I imagine they are hard to find on eBay, at least in decent working order and reasonably priced. All the world's a VAX (32-bit) these days, so nobody cares for 16-bit anymore. But it shouldn't be too hard to look up docs for 286 (e.g. OpenWatcom's FTP site has the 286 and 287 manuals, IIRC). It should list the instruction timings. And from there is shouldn't be hard to measure. It might even be (vaguely) possible to find out all the speeds of every 286 clone in existence. Surely there couldn't have been that many variations.

> (Wikipedia):
> After the 6 and 8 MHz initial releases, it was subsequently scaled
> up to 12.5 MHz. (AMD and Harris later pushed the architecture to
> speeds as high as 20 MHz and 25 MHz, respectively.) On average,
> the 80286 had a speed of about 0.21 instructions per clock.[3]
> The 6 MHz model operated at 0.9 MIPS, the 10 MHz model at 1.5 MIPS,
> and the 12 MHz model at 2.66 MIPS.

As for a delay, not sure, but I'm naively guessing a slow DIV instruction or two would be good enough. Heck, even the 386, at fastest, could only do a single instruction in two clocks, so the 286 must be easier to write slowdown routines. But if you are targeting both real 286s, DOSBox, and modern 4 Ghz AMD64-capable machines, you're probably asking a bit too much.

myrkraverk

13.12.2012, 14:07

@ Rugxulo
 

Waiting for 3 (or 3.3) microseconds? 286 and up.

Please excuse the late reply.

> Is this meant to be widely deployed? I mean, are there real machines that
> will be running this? SoundBlaster is quite rare on actual hardware these
> days except on old machines.

While I'm playing with DOSBox, the code is meant to run on actual hardware too. My previous code examples has been tried with success on actual hardware, both with FreeDOS and a DOS window in OS/2. Some of that was, I believe, hardware emulating the Sound Blaster though.

I've heard there are threads on the fasm forums where people are complaining about DOS software not working in real DOS. I'd rather avoid that trap.

> I think SoundBlaster is dead, I don't think (m)any people will use it.
> Sure, it's well-documented, even fun (I guess, never tried) to program for
> it, but it's fairly useless. Not trying to discourage you, just being
> realistic. :-/

My [possibly false] supposition is that there are still quite a lot of systems with Sound Blaster emulation out there.

> I'm still not sure if you intend this to work on real hardware (actual
> 286?) or just anything you can find (DOSBox). For instance, I don't think
> WinXP supports int 15h, 86h very well. And of course DOSBox isn't accurate
> timings at all. I think every instruction in DOSBox takes only a single
> (virtual) clock cycle. So it's not reliable for timings (and only simulates
> a fast 486 or slow 586, at best).

Yes, I do want this to work on real hardware.

> As for a delay, not sure, but I'm naively guessing a slow DIV instruction
> or two would be good enough. Heck, even the 386, at fastest, could only do
> a single instruction in two clocks, so the 286 must be easier to write
> slowdown routines. But if you are targeting both real 286s, DOSBox, and
> modern 4 Ghz AMD64-capable machines, you're probably asking a bit too much.

There are code examples that may work out. I must confess I've yet to give the solutions I've got a try.

[thanks to everyone who replied]

marcov

13.12.2012, 22:51

@ myrkraverk
 

Waiting for 3 (or 3.3) microseconds? 286 and up.

> > I think SoundBlaster is dead, I don't think (m)any people will use it.
> > Sure, it's well-documented, even fun (I guess, never tried) to program
> for
> > it, but it's fairly useless. Not trying to discourage you, just being
> > realistic. :-/
>
> My [possibly false] supposition is that there are still quite a lot of
> systems with Sound Blaster emulation out there.

Afaik dos games often work under win9x with the emu10k1 (audigy1) cards, when the dos driver initializer app (on the CD) is run in the dos box.

I heard that some people also had gotten it to run with audigy2.

RayeR

Homepage

CZ,
14.12.2012, 01:12

@ marcov
 

Waiting for 3 (or 3.3) microseconds? 286 and up.

> I heard that some people also had gotten it to run with audigy2.

Yes but useless on newer mainboards. And only few early AC97 chips (VIA, AMD Geode) had HW/SMI SB emulation. So SB is dead for many years...

---
DOS gives me freedom to unlimited HW access.

Back to index page
Thread view  Board view
22049 Postings in 2034 Threads, 396 registered users, 243 users online (1 registered, 242 guests)
DOS ain't dead | Admin contact
RSS Feed
powered by my little forum