https://scalibq.wordpress.com/2024/01/01/video-playback-on-low-end-ms-dos-machines/ Scali's OpenBlog(tm) Programming, graphics, hardware, maths, and that sort of thing [path] Skip to content * Home * About * Just keeping it real, a series of articles on oldskool/ retro programming * Thoughts on software development - Bothsidesism... Some results from the modified XDC movie player for 8088/CGA - Video playback on low-end MS-DOS machines Posted on January 1, 2024 by Scali Playing video on MS-DOS... it kinda was a thing with things such as the GRASP/GLPro and FLIC formats back in the day. But not on REALLY low-end machines, such as 8088s and CGA. Until the demo 8088 Corruption was released in 2004. And then its successor 8088 Domination in 2014, which I already discussed earlier. It can play videos at very acceptable quality on an 8088 at 4.77 MHz with (composite) CGA and a Sound Blaster 2.0. Wait, a Sound Blaster 2.0? That is a bit of an anachronism, is it not? Yes, but for two good reasons: 1. The Sound Blaster was the first commonly available sound solution for the IBM PC that could take advantage of the DMA controller for sample playback, thereby reducing CPU load to near-zero. 2. As discussed here earlier, the early Sound Blasters had some shortcomings that meant they weren't capable of seamless playback. The 2.0 version was the first to solve this. Okay, so the Sound Blaster 2.0 was the first *good* solution for digital audio playback. But, one may argue... An IBM PC with a 4.77 MHz 8088 and CGA is not in the 'good' category either, is it? The point is "let's see what we can do anyway". What if we also apply this to audio? I've had this idea for a while. Since I had done a streaming audio solution on PC speaker on my IBM 5160 PC/XT, I wanted to add video as well. It would make sense to combine the two, as I already wrote at the time. And the past few days I had a bit of spare time, so I decided give it a quick-and-dirty try. The code for the XDC encoder and decoder are available on Github, so I just made my own fork: https://github.com/Scalibq/XDC PC Speaker first As you can see from the commits, I added my code for auto-EOI to shave off a few cycles with each interrupt. For the Sound Blaster and 'quiet' options, this is not required, because there is only one interrupt per frame. But for the PC speaker and various other sound sources that don't support DMA, you need an interrupt for every individual sample, so I enable auto-EOI in those cases. I then added some PC speaker routines to enable PWM playback. I use a simple translation table that translates the unsigned 8-bit PCM samples in the XDV file to the correct PWM values. Note that the PWM values are dependent on the sample rate, so the table is initialized at runtime, when the sample rate of the file is known. And that pretty much took care of things already. PC speaker sound is now working. There is extra CPU overhead of course, so you'll either need a faster machine to play the same content, or you need to encode your content with a lower sample rate, and perhaps also reduce the frame rate and/or the MAXDISKRATE value when encoding. I would not recommend PC speaker on a 4.77 MHz machine at all, but on a turbo XT at 8 or 10 MHz, you should be able to get away with it reasonably well, as long as you keep the sample rate low enough, say between 8 and 11 kHz. It will be a bit of trial-and-error to find what works best for your specific machine. Then Covox... Now that I had a solution that could bit-bang samples, I figured I might as well add some other common devices. One is the Covox, which was trivial, as the 8-bit unsigned PCM samples could be sent to the Covox as-is. This makes it slightly faster than the PC speaker solution. It also sounds better. The quality is almost as good as the Sound Blaster. The main issue is that the interrupt-driven sample playback is more susceptible to jitter than DMA transfers are. On a fast enough machine that won't be an issue, but on 8088s you may hear some minor jitter. And Tandy... Tandy is another interesting target. XDC already supports Tandy video modes, so adding Tandy audio would make sense. It can do PC speaker playback of course, but PWM also gives an annoying carrier wave. Sample playback on the SN76496 chip does not have that issue. You can get 4-bit PCM from the sound chip's volume register. The volume is non-linear however, so you can't just take the high nibble from the 8-bit source samples (it works, but it's suboptimal). Instead you should use a translation table from the linear 8-bit samples to the correct non-linear 4-bit volume settings. That's simple enough. I could just use the same approach as for the PC speaker, but with a different table. The Tandy threw me a bit of a curveball though. I had done PCjr sample playback before, so I thought I could just take that code as a starting point. But it gave me a weird low base note, and very loud at that. Strange, that wasn't there before? Where the PCjr uses an actual Texas Instruments SN76496 chip, Tandy opted for a clone instead: an NCR 8496. No big deal, you may think. But there's a catch: for sample playback you set the SN76496 to a period value of 0. This is an undocumented feature that results in an output of 0 Hz, so effectively the output is constantly high. You can modulate it with the volume register, and presto: we have a 4-bit DAC. The NCR 8496 however, didn't get that memo. When you write a period of 0 to it, it interprets it as a value of 400h instead, which is one higher than the maximum period value of 3FFh you can normally set ( which is very similar to the various 6845-implementations that interpret a hsync width of 0 differently). So this results in a tone of (base frequency)/1024, instead of 0 Hz. That explains the loud base note I was hearing. But wait, sample playback works on Tandy, doesn't it? Didn't Rob Hubbard use a sample channel in some of that excellent music he made for Tandy games? Well yes: So how does that work then? I decided to study the code and see what it did exactly. And indeed, I found that it set a period of 1 instead of a period of 0. On the NCR 8496 this results in the highest possible frequency it can play, rather than the lowest possible frequency that you got with a period of 0. Since this frequency is well beyond the audible spectrum (over 100 kHz), you won't actually hear it. But you can hear the modulation with the volume register, giving you the same DAC-like effect (this same trick is also used on the SAA1099 sound chip by the way, found on the CMS/GameBlaster, for example in the game BattleTech: The Crescent Hawk's Revenge). So once I figured out how the NCR 8496 and SN76496 are different, it was an easy enough fix. Apparently all my code was correct after all, I should just have set a period of 1 instead of a period of 0. Sound Blaster.. Sound Baster? That was already supported, wasn't it? Well yes, the 2.0 version. But as I discussed earlier, you can implement streaming playback on earlier Sound Blasters as well. It will not be entirely glitch-free, but you can get acceptable results. In this case I just went for a quick-and-dirty approach. The thing is that the audio buffer size is directly related to the frame rate. That is, an audio buffer has the exact length of one frame. That is what makes the magic work in the original XDC player: the SB will generate an interrupt when the audio buffer has completed. The interrupt handler will then display a new frame. This means that the audio buffers are extremely short. As I said earlier, that is suboptimal, because you get a glitch everytime you start a new buffer. So ideally you want the largest possible buffers, to reduce glitching to a minimum. However, for now I just went for a quick-and-dirty solution that just restarts the audio buffer at every interrupt. It was trivial to add this to the existing code. It will result in a glitch at every frame. However, this glitching is because of the time it takes to start a new buffer on the DSP. When I wrote the article, the assumption was that you use high sample rates. As I wrote in the article, sending a new command to the DSP took about 316 CPU cycles on an 8088 at 4.77 MHz, while a single sample at 22 kHz took 216 CPU cycles. Clearly, at lower sample rates, each sample takes longer, while the DSP time should remain constant. So that means the glitches become less obvious at lower rates, and perhaps won't be noticeable at all, given a low enough rate. I may revisit this code at some point, and modify the buffering of audio in a way that I can apply some of the ideas in my earlier article, by using a large DMA buffer. But for now at least SB 1.x works. And the code checks the DSP version, so it only uses this fallback when a 1.x SB is detected. SB 2.0 and higher should still work as they always did, so they are not compromised in any way by this addition. So now what? Well, one way to use this is to create new content, targeted specifically at lower sample rates that work better on these newly supported sound devices. Another way is to run existing content on faster machines. 8088 Domination uses 22 kHz audio, which is a pretty bad case for bit-banged devices. But a reasonably fast 286 will have no problem with that. So you should be able to play back the 8088 Domination content as-is on a fast enough machine. A third method is to downgrade the existing content. The audio buffers are stored at the end of each frame. It is trivial to downsample the buffers in-place. A turbo XT should be able to play the video content on PC speaker/Covox when the audio is downsampled from 22 kHz to something in the range of 8-11 kHz I would think. And when you modify the audio anyway, you could also pre-apply the translation table, so it won't have to be done at runtime, shaving off some precious CPU-cycles per sample, which will definitely improve performance. Some extra feature flags could be introduced to the feature-byte of the XDV-format to mark PWM-encoded or Tandy-encoded audio. Anyway, feel free to experiment and have fun. See the next post for some of my results, and a modified version of 8088 Domination to toy around with. Share this: * Facebook * X * Like Loading... Related This entry was posted in Oldskool/retro programming, Software development and tagged 8088 Domination, audio, CGA, codec, Covox, demoscene, DOS, electronics, Hornet, music, NCR8496, oldskool, optimize, PC speaker, SB, SN76489, SN76496, Sound Blaster, synthesizer, Tandy, technology, Trixter, video, XDC. Bookmark the permalink. - Bothsidesism... Some results from the modified XDC movie player for 8088/CGA - 2 Responses to Video playback on low-end MS-DOS machines 1. Pingback: Some results from the modified XDC movie player for 8088/CGA | Scali's OpenBlog(tm) 2. Pingback: Futureseek Daily Link Review; 3 January 2024 | Futureseek Link Digest Leave a Reply Cancel reply [ ] [ ] [ ] [ ] [ ] [ ] [ ] D[ ] * Search for: [ ] [Search] * Recent Posts + Some results from the modified XDC movie player for 8088/CGA + Video playback on low-end MS-DOS machines + Bothsidesism... + A small update on downgrading + Implementing live video streaming in software * Recent Comments Video playback on lo... on Some results from the modified... Some results from th... on Video playback on low-end MS-D... Video playback on lo... on 8088 Domination by Trixte... Video playback on lo... on 8088 MPH: The final versi... Video playback on lo... on PC-compatibility, it's a... * Archives + January 2024 + December 2023 + September 2023 + May 2023 + April 2023 + February 2023 + January 2023 + December 2022 + November 2022 + September 2022 + August 2022 + June 2022 + May 2022 + March 2022 + January 2022 + December 2021 + December 2020 + November 2020 + October 2020 + August 2020 + June 2020 + May 2020 + February 2020 + December 2019 + October 2019 + September 2019 + August 2019 + July 2019 + May 2019 + March 2019 + January 2019 + May 2018 + April 2018 + March 2018 + February 2018 + January 2018 + November 2017 + October 2017 + May 2017 + March 2017 + February 2017 + January 2017 + December 2016 + August 2016 + July 2016 + June 2016 + May 2016 + April 2016 + March 2016 + February 2016 + January 2016 + December 2015 + November 2015 + September 2015 + August 2015 + June 2015 + April 2015 + January 2015 + December 2014 + November 2014 + October 2014 + September 2014 + August 2014 + July 2014 + June 2014 + April 2014 + March 2014 + February 2014 + December 2013 + November 2013 + October 2013 + September 2013 + August 2013 + July 2013 + June 2013 + May 2013 + April 2013 + March 2013 + February 2013 + January 2013 + December 2012 + November 2012 + October 2012 + September 2012 + August 2012 + July 2012 + June 2012 + May 2012 + April 2012 + March 2012 + February 2012 + January 2012 + December 2011 + November 2011 + October 2011 + September 2011 + August 2011 + July 2011 + June 2011 + May 2011 + March 2011 + February 2011 + January 2011 + December 2010 + November 2010 + October 2010 + September 2010 + August 2010 + July 2010 + June 2010 + May 2010 + April 2010 + March 2010 + February 2010 + January 2010 + December 2009 + November 2009 + October 2009 + September 2009 + August 2009 + July 2009 + June 2009 + May 2009 + January 2009 * Categories + Direct3D + Hardware news + Oldskool/retro programming + OpenCL + OpenGL + Science or pseudoscience? + Software development + Software news + Uncategorized + Vulkan * Meta + Register + Log in + Entries feed + Comments feed + WordPress.com Scali's OpenBlog(tm) Create a free website or blog at WordPress.com. [Close and accept] Privacy & Cookies: This site uses cookies. By continuing to use this website, you agree to their use. To find out more, including how to control cookies, see here: Cookie Policy * Comment * Subscribe Subscribed + [wpcom-] Scali's OpenBlog(tm) Join 135 other subscribers [ ] Sign me up + Already have a WordPress.com account? Log in now. * + [wpcom-] Scali's OpenBlog(tm) + Customize + Subscribe Subscribed + Sign up + Log in + Copy shortlink + Report this content + View post in Reader + Manage subscriptions + Collapse this bar %d [b]