• Please review our updated Terms and Rules here

Please help me to play high-quality audio on XT/286 machine

rallen

Member
Joined
Feb 27, 2024
Messages
21
Hello Forum!

I'm looking for a vintage PC hobbyist interested in running a DOS program that plays high-quality audio through a PC speaker.

Context: I'm a vintage software hobbyist writing graphics/sound/other software using 20-30-year-old programming languages/compilers for fun. I wrote an assembly program that plays a high-quality digitized music sample loop (5-second intro section of Free by Ultra Nate) through the speaker, and it sounds amazing and almost unreal on emulators such as DOSBox, 86box, and Qemu. I would love to know if this program works and sounds the same good on real hardware, but unfortunately, I don't have one.

If you can run the program on an XT/286/386 machine, record sound from the speaker on your smartphone or other device, and post the sound/video file back, it would be fantastic!
What you get: +1000 to Karma, a good mood from the music, and an understanding of how much the old hardware is capable of :)
What I get: satisfaction from knowing how my program sounds on a real PC :)

The archive with the .com program that plays the music is attached to the message.

Thank you!
 

Attachments

  • PCS_PWM.zip
    22.2 KB · Views: 8
Tried on my original XT just now. It didn't work. I copied it from floppy to HDD (XT-IDE CF card) and when run it makes a slight quiet speaker click sound (I think, or I imagined it!) and then locks up. Ctl-Alt-Del didn't work, I had to power cycle.
 
Tried on my original XT just now. It didn't work. I copied it from floppy to HDD (XT-IDE CF card) and when run it makes a slight quiet speaker click sound (I think, or I imagined it!) and then locks up. Ctl-Alt-Del didn't work, I had to power cycle.

Thank you so much for trying, @Chr$ ! It sounds like the original XT's 4.77Mhz CPU speed is insufficient for running the timer interrupts at 8KHz.
This is sad. It would be great if this program worked on the original XT.

It looks like we need to try a faster machine.
If you or somebody else in the thread have 286 with a 12Mhz+ CPU (I've read that 286 had 8, 10, and 12.5 MHz and later editions 20 Mhz CPUs), it would be great to try it on.
The code is 100% working and has been tested on multiple emulators; unfortunately, emulators are not too precise regarding CPU speed. That's where we need a bit of experimentation.
Let's not give up, please! 💪
 
Thank you so much for trying, @Chr$ ! It sounds like the original XT's 4.77Mhz CPU speed is insufficient for running the timer interrupts at 8KHz.
This is sad. It would be great if this program worked on the original XT.

It looks like we need to try a faster machine.
If you or somebody else in the thread have 286 with a 12Mhz+ CPU (I've read that 286 had 8, 10, and 12.5 MHz and later editions 20 Mhz CPUs), it would be great to try it on.
The code is 100% working and has been tested on multiple emulators; unfortunately, emulators are not too precise regarding CPU speed. That's where we need a bit of experimentation.
Let's not give up, please! 💪
I also have a 286 or 2 but they aren't setup at the moment.
 
8Khz should be (barely) doable on a 4.77Mhz XT. What does your timer ISR look like?
It is actually extremely lightweight (no loops or heavy math, at least)
Code:
new_int8:
    push    ds
    push    ax
    push    bx
    push    si

    lds     bx, [data_ptr]       ; loading sample pointer to ds:bx
    mov     si, [offset]         ; sample offset
    mov     ah, [ds:bx+si]       ; loading sample byte
    shr     ah, 1                ; take the highest 7 bits

    mov     al, 0xb0             ; set sound frequency
    out     0x43, al
    mov     al, ah
    out     0x42, al
    xor     al, al
    out     0x42, al

    inc     word [offset]        ; advance to the next byte of the sample

    cmp     si, data_size        ; are we at the end of the sample?
    jl      update_bios_timer
    mov word [offset], 0         ; reset offset to start of the sample

update_bios_timer:
    mov     ax, [subtick]
    add     ax, frequency
    mov     [subtick], ax
    jnc     no_bios_update
    mov     bx, bios_tick
    pushf
    call    far [ds:bx]          ; call the actual timer tick in bios
    jmp     int_exit

no_bios_update:
    mov     al, 0x20
    out     0x20, al

int_exit:
    pop     si
    pop     bx
    pop     ax
    pop     ds
    iret
 
Last edited:
Chr$ said:
it makes a slight quiet speaker click sound (I think, or I imagined it!) and then locks up. Ctl-Alt-Del didn't work, I had to power cycle.
Exact same behavior on my PCjr too, so it wasn't an illusion - a pop in the speaker and the machine locks up solid. Now, if I introduce a RAM expansion to the system bus, the program overflows through the video buffer and starts writing gibberish and ASCII BEL characters, waking the neighbors up with the loud honking.

rallen said:
4.77Mhz CPU speed is insufficient for running the timer interrupts at 8KHz.
It's not about the system clock, the application crashes on an 8088 outright. Beware that CPU does not support all realmode opcodes a 286 does: shl/shr needs to be in cx if not 1, push sp behaves differently, and so on.
For an emulator, I use MESSUI (MAME32). It has its own bugs, but will emulate a 5150/XT/Jr with some peripherals, and is quite speed-adequate. I like how it even simulates the floppy drive spindle and stepper motor sounds. :)
 
Last edited:
Exact same behavior on my PCjr too, so it wasn't an illusion - a pop in the speaker and the machine locks up solid. Now, if I introduce a RAM expansion to the system bus, the program overflows through the video buffer and starts writing gibberish and ASCII BEL characters, waking the neighbors with the loud honking.


It's not about the system clock, the application crashes on an 8088 outright. Beware that CPU does not support all realmode opcodes a 286 does: shl/shr needs to be in cx if not 1, push sp behaves differently, and so on.
For an emulator, I use MESSUI (MAME32). It has its own bugs, but will emulate a 5150/XT/Jr with some peripherals, and is quite speed-adequate.
Thanks @deanimator !
I'm compiling the code with the 8086 instruction set configuration. Well, at least I instruct yasm to stick to it.
However, I'm not sure the opcodes produced are 100% 8086 CPU compatible. I've checked a few of the most suspicious instructions against the Intel 8086 CPU manual, and their opcodes are valid.
I will look at MAME32 and see if the program works.
I like how it even simulates the floppy drive spindle and stepper motor sounds. :)
Okay, I definitely want to hear this! :giggle:
Thanks again!
 
It is actually extremely lightweight (no loops or heavy math, at least)
Code:
new_int8:
    push    ds
    push    ax
    push    bx
    push    si

    lds     bx, [data_ptr]       ; loading sample pointer to ds:bx
    mov     si, [offset]         ; sample offset
    mov     ah, [ds:bx+si]       ; loading sample byte
    shr     ah, 1                ; take the highest 7 bits

    mov     al, 0xb0             ; set sound frequency
    out     0x43, al
    mov     al, ah
    out     0x42, al
    xor     al, al
    out     0x42, al

    inc     word [offset]        ; advance to the next byte of the sample

    cmp     si, data_size        ; are we at the end of the sample?
    jl      update_bios_timer
    mov word [offset], 0         ; reset offset to start of the sample

update_bios_timer:
    mov     ax, [subtick]
    add     ax, frequency
    mov     [subtick], ax
    jnc     no_bios_update
    mov     bx, bios_tick
    pushf
    call    far [ds:bx]          ; call the actual timer tick in bios
    jmp     int_exit

no_bios_update:
    mov     al, 0x20
    out     0x20, al

int_exit:
    pop     si
    pop     bx
    pop     ax
    pop     ds
    iret

I see potential issues with segment management. You're best off by specifying explicit segment overrides in an ISR.
 
@Chuck(G) you are right! I also noticed that I was referencing vars without explicitly setting DS. Getting older and not noticing obvious things 🤦‍♂️
I have fixed this, rewrote the whole thing, and compiled it with Turbo Assembler v1.0 from 1989 instead of modern Yasm.
I also removed a few optimizations and replaced them with simpler instructions.

Gentleman, could you please re-run the test with the version attached if you still have hope in me :LOL:
 

Attachments

  • SOUND2.zip
    22.2 KB · Views: 5
It is actually extremely lightweight (no loops or heavy math, at least)
Code:
new_int8:
    push    ds
    push    ax
    push    bx
    push    si

    lds     bx, [data_ptr]       ; loading sample pointer to ds:bx
    mov     si, [offset]         ; sample offset
    mov     ah, [ds:bx+si]       ; loading sample byte
    shr     ah, 1                ; take the highest 7 bits

    mov     al, 0xb0             ; set sound frequency
    out     0x43, al
    mov     al, ah
    out     0x42, al
    xor     al, al
    out     0x42, al

    inc     word [offset]        ; advance to the next byte of the sample

    cmp     si, data_size        ; are we at the end of the sample?
    jl      update_bios_timer
    mov word [offset], 0         ; reset offset to start of the sample

update_bios_timer:
    mov     ax, [subtick]
    add     ax, frequency
    mov     [subtick], ax
    jnc     no_bios_update
    mov     bx, bios_tick
    pushf
    call    far [ds:bx]          ; call the actual timer tick in bios
    jmp     int_exit

no_bios_update:
    mov     al, 0x20
    out     0x20, al

int_exit:
    pop     si
    pop     bx
    pop     ax
    pop     ds
    iret

If you want to see how really efficient code looks like (no offence) you might want to take a look at mushroom.com, that plays commercial ad jingle at 18KHz on 4.77Mhz XT, with 2X interpolation from 9Khz sample.
I did deep research some time ago, results are here:
https://github.com/jinshin/UberPWM/
--
For personal testing you can use MartyPC emulator, that is 100% PC XT accurate.
 
Last edited:
Tried it. Didn't work. Same as this:
when run it makes a slight quiet speaker click sound (I think, or I imagined it!) and then locks up. Ctl-Alt-Del didn't work, I had to power cycle.
Except for the fact that I could in fact restart the system using ctrl-alt-del.

I recorded a video of the attempt:

 
Last edited:
Sorry guys, I can read your comments, but my comments are delayed because I'm new on the forum, and you can see my messages only after the moderator review.

@boggit, thanks for trying and recording the video! Unfortunately, we've identified a bug that is fixed in the version attached to this comment in my post #12.
It would be awesome if you could run this new version on your machine. Thank you!

@n0p it seems that your code uses the same approach.
However, my program preserves the timer, loops the sample, and checks if any keys are pressed.
It is not about efficiency but functionality. Thanks for sharing, anyway.
I've started to play with martypc, but it takes time because it needs to be configured manually.
 
Way back when I had a program that could play an arbitrary sample file over the PC XT speaker and it sounded pretty good. I seem to remember the example file I had to run with it was "Start Me Up" by the Stones. It was quite quiet though. I also built a sampler that worked off the parallel port. That was a turbo XT but I think the program was supposed to work on 4.77MHz. Not much help but just to say it is definitely possible.
 
@boggit, thanks for trying and recording the video! Unfortunately, we've identified a bug that is fixed in the version attached to this comment in my post #12.
It would be awesome if you could run this new version on your machine. Thank you!

1. Done!

2. It works!


3...after a fashion. The high-pitched noise accompanying the musical notes is very, very grating for my poor ears. 😅
 
Last edited:
This is on a 8088-10MHz with a common, period correct speaker of a clone. Volume is very low, but this is what it really sounds like. My mobile was right in front of the case and you can hear the PSU fan (which is the only fan and on the opposite side) much louder.
Now if you excuse me, my ears hurt!
 

Attachments

  • 8088-10.zip
    227.6 KB · Views: 7
Back
Top