; Ex10_1.asm
;
; Software that plays Musical notes.
; Intended for use with Chapter 10 Laboratory Experiments


		.xlist
		include 	stdlib.a
		includelib	stdlib.lib
		.list

; Hardware related equates

PPI_B		equ	61h		;8255 port B address
PIT_Ch2		equ	42h		;Timer channel 2 address
PIT_CW		equ	43h		;Timer control word address.

RTC		textequ	<es:[6ch]>	;Real Time clock writes here.





; The following macros program the 8254 timer chip to play a
; note or a rest.

Note		macro	frequency, duration
		mov	ax, frequency
		mov	cx, duration
		call	PlayNote
		endm

Rest		macro	Duration
		local	RestLoop

;; Turn off the speaker.

		in	al, PPI_B
		and	al, 0FCh
		out	PPI_B, al

		mov	cx, Duration
RestLoop:	call	Delay18
		loop	RestLoop
		endm




dseg		segment	para public 'data'

; See Delay18.asm for details about the following variables.

TimedValue	word	0
RTC2		word	0


; DivisorConst is the constant that, when divided by a desired frequency
; produces the divisor constant to feed into the timer chip to produce
; a tone of the given frequency.

DivisorConst	dword	1193180


dseg		ends

;********************************************************





cseg		segment	para public 'code'
		assume	cs:cseg, ds:dseg

; Delay- This routine delays for approximately 1/18th second.
; 	 See the Delay18.asm file for comments about this code.

Delay18		proc	near
		push	ds
		push	es
		push	ax
		push	bx
		push	cx
		push	dx
		push	si

		mov	ax, dseg
		mov	es, ax
		mov	ds, ax
		mov	cx, TimedValue
		mov	si, es:RTC2
		mov	dx, PPI_B

		align	4
TimeRTC:	mov	bx, 50
		align	4
DelayLp:	dec	bx
		jne	DelayLp
		cmp	si, es:RTC2
		loope	TimeRTC

		pop	si
		pop	dx
		pop	cx
		pop	bx
		pop	ax
		pop	es
		pop	ds
		ret
Delay18		endp



; DelayInit-	Computes the timing constant for the Delay18 routine
;		by watching when the RTC code updates the RTC variable.

DelayInit	proc
		push	es
		push	ax
		push	bx
		push	cx
		push	dx
		push	si

		mov	ax, 40h
		mov	es, ax
		mov	ax, RTC
RTCMustChange:	cmp	ax, RTC
		je	RTCMustChange

		mov	cx, 0
		mov	si, RTC
		mov	dx, PPI_B
		align	4
TimeRTC:	mov	bx, 50
		align	4
DelayLp:	dec	bx
		jne	DelayLp
		cmp	si, RTC
		loope	TimeRTC

		neg	cx			;CX counted down!
		shl	cx, 1			;Increase delay time.
		mov	TimedValue, cx		;Save away

		pop	si
		pop	dx
		pop	cx
		pop	bx
		pop	ax
		pop	es
		ret
DelayInit	endp





; PlayNote-	This procedure programs the 8254 timer chip to play a given
;		note (frequency is passed in AX) for a given duration (passed
;		in cx).

PlayNote	proc
		push	dx
		push	cx
		push	ax

; Turn on the speaker.

		in	al, PPI_B
		or	al, 3
		out	PPI_B, al

; Tell the chip we want to program it with a new 16-bit divisor value.

		mov	al, 0B6h	;Magic value to program chip.
		out	PIT_CW, al

; Convert the frequency to a divisor constant.

		pop	cx		;Get the frequency.

		mov	dx, word ptr DivisorConst+2
		mov	ax, word ptr DivisorConst
		div	cx

		out	PIT_Ch2, al
		xchg	al, ah
		out	PIT_Ch2, al

		mov	ax, cx		;Restore original AX value.
		pop	cx		;Retrieve duration
		mov	dx, cx		;Save Duration.
DelayLp:	call	Delay18
		loop	DelayLp
		mov	cx, dx		;Restore original CX value

		pop	dx		;Restore original DX value.
		ret
PlayNote	endp


; Main program which tests out the DELAY subroutine.

Main		proc
		mov	ax, dseg
		mov	ds, ax
		mov	es, ax

		call	DelayInit

; Play "Amazing Grace"

		note	294, 4
		note	392, 8
		note	494, 2
		note	392, 2
		note	494, 8
		note	440, 4
		note	392, 8
		note	330, 4
		note	294, 8
		rest	2
		note	294, 4
		note	392, 8
		note	494, 2
		note	392, 2
		note	494, 8
		note	440, 4
		note	587, 16

		note	494, 4
		note	587, 4
		note	494, 2
		note	587, 2
		note	494, 2
		note	392, 8
		note	294, 4
		note	330, 4
		note	392, 2
		rest	2
		note	392, 2
		note	330, 2
		note	294, 8
		rest	2
		note	294, 4
		note 	392, 8
		note	494, 2
		note	392, 2
		note	494, 8
		note	440, 4
		note	392, 16


		rest	1



Quit:		ExitPgm			;DOS macro to quit program.
Main		endp







cseg            ends



sseg		segment	para stack 'stack'
stk		dw	1024 dup (0)
sseg		ends
		end	Main
