; Program: Simple Note Player by MATRIX
org 256
call setup_pit_timer

mov si,msg
call Print
mov bx,tonedata
call Play

call reset_pit_timer
int 20h

;************************************************************
; Procedure print
; prints a zero terminated string pointed to by si
;************************************************************

Print:
push ax
mov ah,14; BIOS code for screen display
cld
print_loop:lodsb; moving the character to be displayed to al
or al, al; checking if the char is NULL
jz printdone
int 10h; Calling BIOS routine
JMP print_loop
printdone:pop ax
ret
; End of print procedure...

;************************************************************
; Procedure Play
; plays a sick and demented melody
;************************************************************
Play:
pusha	       ;save regs
localloop:
mov ax,[bx]    ;load sound data
add bx,2
or ax,ax
jz finished
jns notdelay
delay:
push bx
neg ax
mov cx,ax
call pit_tick_delay
pop bx
jmp localloop
notdelay: ; not delay, its a note
push bx
call sound  ;play it
pop bx
jmp localloop
finished:
call nosound
popa
ret
;end Play

nosound: ; Silences the speaker.
in al,0x61
and al,0xFC
out 0x61,al
ret

sound: ; AX = frequency Starts the speaker emiting a sound of a given frequency
mov bx,ax ; RETURNS:  AX,BX,DX = undefined
mov dx,0x12
mov ax,0x34DD ; ;mov ax,0x34DC ; which is more accurate?
div bx
mov bl,al
mov al,0xB6
out 0x43,al
mov al,bl
out 0x42,al
mov al,ah
out 0x42,al
in al,0x61
or al,3
out 0x61,al
ret

pit_tick_delay:  ; waits cx ticks, destroys: ax bx
;cli ;if you need accurate timing uncomment this
  call	  read_pit_value
 mov	 bx,ax
.below:
  call	  read_pit_value
 cmp	 ax,bx
   jl	  .below
.above:
  call	  read_pit_value
 cmp	 ax,bx
   jg	   .above
 loop	 .below
;sti ;if you need accurate timing uncomment this
ret

reset_pit_timer:
 mov al,00110110b ; count down twice generator
jmp setupcommon
setup_pit_timer:  ; returns status in al
 mov al,00110100b ; rate generator
setupcommon:
cli ;if you need accurate timing comment this refer to above
 out 43h,al
 xor al,al
 out $40,al
 out $40,al
.waitlatch:
 mov al,11100010b ; get timer 0 status
  out 43h,al
  in al,40h
bt ax,6
jc .waitlatch
sti ;if you need accurate timing comment this refer to above
ret

read_pit_value:
;.waitlatch:                                  ; if you have early 8253s comment
; mov al,11100010b ; get timer 0 status       ; this part out
; out 43h,al                                  ; ( wais for latch to be valid )
; in al,40h                                   ;
;bt ax,6                                      ;
;jc .waitlatch                                ;
  mov al,11010010b
cli
  out 43h,al
  in al,40h
  mov ah,al
  in al,40h
sti
  xchg al,ah
ret

msg: db 'Playing sound',0
tonedata: ; negatives are delay
dw 440,-7,880,-4,440,-4,330,-15
dw 440,-7,880,-4,440,-4,330,-15
dw 550,-7,440,-4,330,-4,440,-15
dw 0 ; stop !

; Make the file 512 bytes long
times 512-$ Db 0 ; file is 256 bytes? FASM bug?
;times 512 DB 0