First post, by furrykef
I wasn't too sure which forum to put this in, so I'm putting it here. Please move as necessary. (Maybe it should go in one of the DosBox forums, but I'm also interested in how this behaves on a real Tandy...)
I'm writing an NSF player -- y'know, Nintendo music -- for the Tandy 1000 and IBM PCjr. So far this has worked splendidly except for one thing: the SN76489 kinda sucks at being a 2A03. Not only do the squares have no duty cycle and triangles are not possible, the bottom octave is missing. For the NES triangle channel, the bottom two octaves are missing, so many songs have no bass. I find this unacceptable.
So I thought, OK, I'll just set the frequency to zero and toggle the volume to play arbitrary waveforms manually. (This is not unlike the "RealSound" technology used for the PC speaker.) This almost works in DosBox 0.74. I wrote a test program that hooks the 1Ch (or 08h) interrupt and plays a square wave by toggling whenever the timer fires. The problem is, there are gaps in the square wave where the peak or trough is about twice as long as it should be. The result sounds like some kind of really bad arpeggio instead of a solid square wave. I have confirmed that DosBox is running literally nothing aside from my timer routine, so it's not like some other interrupt is interfering. Upon thinking about it further, I think what's happening is that the system clock is slightly out of sync with the SN76489's clock, so sometimes the volume is toggled before the SN's timer fires and sometimes it's toggled after, hence the apparent skips. However, I'm not 100% sure on this yet.
I'm trying to determine if this is a fault in DosBox or if the real hardware behaves this way. I have attached my test program below. (Note that there is no way to kill it, so it will require a reboot.) It would be nice if somebody could tell me how it sounds on a real Tandy. If a real Tandy produces a solid square, then we can work on fixing DosBox to produce the same result.
To assemble with NASM: nasm test.asm -fbin -o test.com
cpu 8086org 0x100section .textstart:; Set up multiplexerin al, 0x61or al, 0x60out 0x61, al; Set channel freq to 0 (flat output)mov al, 0x80out 0xc0, alxor al, alout 0xc0, al; Silence channelmov al, 0x9Fout 0xc0, al; Kill all interrupts for now; (Note: in a real program, we'd need to restore them upon exit)mov al, 0xffout 0x21, al; Install timer ISR; NB: In actual program, will have to uninstall ISR afterwards; Remember DS == CS, so DS:DX == CS:DX;mov dx, ISR;mov ah, 0x25 ; DOS service 0x25 = install ISR;mov al, 0x08 ; 0x08 = system timer interrupt;int 0x21; Install timer ISR the hard wayxor ax, axmov es, axmov ax, ISRmov [ES:8*4], axmov ax, csmov [ES:8*4+2], ax; Set clock; NB: In actual program, will have to reset clock when done; (same code, just send 0's to 0x40 instead); Then also set DOS to real time clockmov al, 0b00111010 ; @TODO@ -- which mode (bits 1-3)?out 0x43, almov al, 0x70 ; Low bitsout 0x40, almov al, 4out 0x40, al ; High bits; Enable just the timer interruptmov al, 0xfeout 0x21, al; 'Forever' is brought to you by Pinkie Pieforever:
jmp foreverISR:clipush dspush ax; Set DS to CSpush cspop dsmov al, [SquareToggle]not almov [SquareToggle], aland al, 0x0for al, 0x90out 0xc0, alpop ds; Tell 8259A interrupt donemov al, 0x20out 0x20, alpop axstiiretSquareToggle: db 0