Producing Sound on the Internal Speaker
SND produces sound on the speaker.
Version: 1.00b
Author: Ben Lunt (Forever Young Software)
Date: 09 Dec 1998
Assembler: NBASM
SND sends the freq's specified in a buffer to the speaker for a duration of the next word in the buffer (1-16 where 16 = one second).
This allows you to specify any freq. and duration wanted to produce sounds from a simple rhyme to a complex Bomb sound.
You can use this routine in your game with little or no trouble.
You can have an 'unlimited' (less than 64k) number of buffers holding different sound data. All you have to do is point SI to this buffer and then call it.
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; This routine shows how to send sound (freq.) to the internal speaker.
; You can sound a frequency between 1 and 4000+. Please Note that the
; human ear has a hard time hearing a frequency less than about 440.
; I use a timer function to wait for the duration. I also have
; the freq. and duration in a buffer and get a single freq. and duration
; value each time. This is so that you can make quite a few different
; sounds and just point SI to that buffer and then call this routine.
; The 00h,00h (asciiz) at the end of the buffer tells this routine to
; quit.
;
.model tiny
.code
org 100h
start: push ds
pop es
mov si,offset SomeTune
mov dx,61h ; turn speaker on
in al,dx ;
or al,03h ;
out dx,al ;
mov dx,43h ; get the timer ready
mov al,0B6h ;
out dx,al ;
LoopIt: lodsw ; load desired freq.
or ax,ax ; if freq. = 0 then done
jz short LDone ;
mov dx,42h ; port to out
out dx,al ; out low order
xchg ah,al ;
out dx,al ; out high order
lodsw ; get duration
mov cx,ax ; put it in cx (16 = 1 second)
call PauseIt ; pause it
jmp short LoopIt
LDone: mov dx,61h ; turn speaker off
in al,dx ;
and al,0FCh ;
out dx,al ;
.exit ; exit to DOS
; =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; This routine waits for a specified amount of milliseconds (within 50ms)
; Since I want to keep it simple, I am going to use the BIOS timer tick
; at 0040:006Ch. It increments 18.2 times a second.
; (1000 milliseconds divided by 18.2 = ~55ms)
; This is not a very good delay. Depending on when it is called,
; it could delay up to 110ms. However it will always delay at
; least 55ms.
; If you do much with this, you will need a much better delay routine.
; You can code a delay using the RDTSC instruction, if you know you
; have a CPU that supports that instruction (all modern CPU's do).
; There are many other delay techniques to choose from.
PauseIt proc near uses ax cx es
mov ax,0040h
mov es,ax
; wait for it to change the first time
mov al,es:[006Ch]
@@: cmp al,es:[006Ch]
je short @b
; wait for it to change again
loop_it: mov al,es:[006Ch]
@@: cmp al,es:[006Ch]
je short @b
sub cx,55
jns short loop_it
ret
PauseIt endp
SomeTune dw 1397,08
dw 1397,08
dw 1397,08
dw 1318,06
dw 1244,16
dw 1046,04
dw 1108,04
dw 1174,04
dw 1244,04
dw 1174,08
dw 1244,08
dw 1318,08
dw 1396,08
dw 1480,08
dw 1568,16
dw 00h,00h
.end
NOSOUND is a TSR that 'silences' the sound on the speaker.
Version: 1.00b
Author: Ben Lunt (Forever Young Software)
Date: 09 Dec 1998
Assembler: NBASM
To use this routine, enter NOSOUND ON at the DOS prompt. To allow sound again, enter NOSOUND OFF.
The routine stays resident, so you can call NOSOUND with the ON or OFF parameters as much as you would like.
; use NOSOUND ON to silence the speaker
; use NOSOUND OFF to 'un' silence the speaker
;
.model tiny
.code
org 100h
start: jmp install
Copyright db 13,10,'NOSOUND Quiets the hardware speaker.'
db 13,10,'Copyright 1984-2024 Forever Young Software',13,10,36
AllowY db 13,10,' Allowing Sound$'
AllowN db 13,10,' Not Allowing Any Sound$'
Old1CAdr dw 00h ; these remember the original Int 1Ch address
Old1CSeg dw 00h ; they must be in the code segment
Old16Adr dw 00h ; these remember the original Int 16h address
Old16Seg dw 00h ; they must be in the code segment
job db 00h ; 0 = sound off, else sound on
NewInt1Ch: cli ; disable interrupts
mov al,cs:job ; if job != 0 then skip ours
or al,al ; (need to use fast instructions)
jnz short SoundOn ; ('cmp mem,immed' is just to slow)
push dx ;
; the in and out instructions are extremely slow on so-called
;'faster' machines (386, 486, 586 (pentiums)) so this is why you
; still hear a little bit of sound at first.
mov dx,61h ; turn sound off
in al,dx ; .
and al,0FCh ; clear bits 1 & 0
out dx,al ; .
pop dx ;
SoundOn: sti ; reenable interrupts
jmp far cs:Old1CAdr
NewInt16h: cli ; disable interrupts
cmp ah,66h ; if our service number
jne short SkipOurs
mov cs:job,al ; then put 'job' in job above
mov al,ah ; send installed flag
sti ; and return to NOSOUND.COM
iret ;
SkipOurs: sti ; reenable interrupts
jmp far cs:Old16Adr ;
Install: mov dx,offset Copyright ; print message
mov ah,09h ;
int 21h ;
mov ah,62h ; get PSP segment
int 21h ;
mov es,bx ;
xor al,al ; assume no sound
mov dx,offset AllowN ; print message
mov ah,es:[0083h] ; get command line 'n' or 'f'
cmp ah,'n' ; if 'n' then job != 0
jne short SoundOff1 ;
mov al,0FFh ;
mov dx,offset AllowY ; print message
SoundOff1: push ax ; save al
mov ah,09h ;
int 21h ;
pop ax ; restore al
mov ah,66h ; call interrupt 16h w/our service #
int 16h ; on return:
cmp al,66h ;
jne short NotInstld ; if al = 66h, then is installed
mov ah,4Ch ; and exit (no TSR it)
int 21h ;
NotInstld: mov ah,62h ; free environment string
int 21h ;
mov es,bx ;
mov bx,2Ch ;
mov ax,es:[bx] ;
mov es,ax ;
mov ah,49h ;
int 21h ;
mov ax,351Ch ; ask DOS for the existing Int 1Ch vector address
int 21h ; DOS returns the segment:address in ES:BX
mov cs:Old1CAdr,bx ; save it locally
mov cs:Old1CSeg,es
mov ax,251Ch ; point Interrupt 1Ch to our own handler
mov dx,offset cs:NewInt1Ch
push cs ; copy CS into DS
pop ds
int 21h
mov ax,3516h ; ask DOS for the existing Int 16h vector address
int 21h ; DOS returns the segment:address in ES:BX
mov cs:Old16Adr,bx ; save it locally
mov cs:Old16Seg,es
mov ax,2516h ; point Interrupt 16h to our own handler
mov dx,offset cs:NewInt16h
push cs ; copy CS into DS
pop ds
int 21h
mov dx,offset install ; save all TSR code + PSP
sub dx,offset start
sub dx,271
mov cl,04h ; + 15 bytes to make sure we get all of TSR
shr dx,cl ; (paragraphs)
mov ax,3100h ; exit to DOS but stay resident
int 21h ;
.end start
Please let me know if there are any errors or if this routine doesn't work on your machine. On faster machines
(about 133mhz and faster), this routine might not work.
Please read the .ASM file for distribution rules and copyrights.