;This program demonstrates how to use an AdLib-compatible sound card's FM ;chip to play MIDI-style music. It plays a regular scale from C to C. It ;should work with any Sound Blaster or AdLib clone, as the ports it uses are ;quite standard. No IRQs or DMAs are used, just plain hardware I/O ports. ;Before we begin, we need to prepare the channels we will use by setting ;the 4 register types for both the carrier and the modulator, meaning each ;channel has a total of 8 registers we must set. The 4 register types are: ;AM/vibrato/octave shift, key scaling level/output level, attack rate/decay ;rate, and sustain level/release time. ;All registers here are for channel 1, as it's the only channel used by this ;program. MOV AL,20h ;Set AM/vibrato/octave register for carrier. MOV AH,1 ;Play note at specified octave, no AM or vibrato. CALL SetReg MOV AL,23h ;Set AM/vibrato/octave register for modulator. MOV AH,1 ;Play note at specified octave, no AM or vibrato. CALL SetReg MOV AL,40h ;Set key scaling level/output level register for carrier. MOV AH,00011111xB ;Scaling level of 0, fairly loud output level. CALL SetReg MOV AL,43h ;Set key scaling level/output level register for modulator. MOV AH,0 CALL SetReg MOV AL,60h ;Set attack rate/decay rate register for carrier. MOV AH,11100100xB ;High attack rate, low decay rate. CALL SetReg MOV AL,63h ;Set attack rate/decay rate register for modulator. MOV AH,11100100xB ;High attack rate, low decay rate. CALL SetReg MOV AL,80h ;Set sustain level/release time register for carrier. MOV AH,10011101xB ;Medium sustain level, short release time. CALL SetReg MOV AL,83h ;Set sustain level/release time register for modulator. MOV AH,10011101xB ;Medium sustain level, short release time. CALL SetReg ;INITIAL CHANNEL PREPARATION IS COMPLETE, AND WE ARE NOW READY TO BEGIN ;PLAYING NOTES!!! ;Begin C MOV AL,0A0h MOV AH,10101110xB CALL SetReg MOV AL,0B0h MOV AH,00101010xB CALL SetReg CALL WaitNote ;End C ;Begin D MOV AL,0A0h MOV AH,10000001xB CALL SetReg MOV AL,0B0h MOV AH,00101101xB CALL SetReg CALL WaitNote ;End D ;Begin E MOV AL,0A0h MOV AH,10110000xB CALL SetReg MOV AL,0B0h MOV AH,00101101xB CALL SetReg CALL WaitNote ;End E ;Begin F MOV AL,0A0h MOV AH,11001010xB CALL SetReg MOV AL,0B0h MOV AH,00101101xB CALL SetReg CALL WaitNote ;End F ;Begin G MOV AL,0A0h MOV AH,00000010xB CALL SetReg MOV AL,0B0h MOV AH,00101110xB CALL SetReg CALL WaitNote ;End G ;Begin A MOV AL,0A0h MOV AH,01000001xB CALL SetReg MOV AL,0B0h MOV AH,00101110xB CALL SetReg CALL WaitNote ;End A ;Begin B MOV AL,0A0h MOV AH,10000111xB CALL SetReg MOV AL,0B0h MOV AH,00101110xB CALL SetReg CALL WaitNote ;End B ;Begin C MOV AL,0A0h MOV AH,10101110xB CALL SetReg MOV AL,0B0h MOV AH,00101110xB CALL SetReg CALL WaitNote ;End C MOV AX,4C00h ;terminate program INT 21h SetReg: ;This routine sets a register on the sound card. The procedure to do this is ;fairly simple: First, output the value of the register you want to write to ;onto I/O port 388h. Then, output the value to write to that register onto ;port 389h. ;Inputs: AL is the register to be written to, AH is the value to write to it. MOV DX,388h OUT DX, AL MOV AL,AH MOV DX,389h OUT DX, AL RET WaitNote: ;This routine waits a moment after the note begins, then shuts the note off ;and waits a little more to give the sound card time to recover. ;Here begins the first time-wait routine, used when the note begins. MOV AH,0 INT 1Ah ADD DX,10 MOV BX,DX looper: INT 1Ah CMP DX,BX JNE looper ;Here ends the first time-wait routine. MOV AL,0B0h ;Set register B0... MOV AH,0 ;...to 0, thus turning the note off. CALL SetReg ;Here begins the second time-wait routine, used after the note ends. MOV AH,0 INT 1Ah ADD DX,10 MOV BX,DX looper2: INT 1Ah CMP DX,BX JNE looper2 RET