data segment
c equ 523                       ;Frequenzen der Tne
d equ 587
e equ 659
f equ 698
g equ 784
a equ 880
h equ 988

Song:     dw c,250, d,250, e,250, f,250, g,500, g,500
          dw a,250, a,250, a,250, a,250, g,500
          dw a,250, a,250, a,250, a,250, g,500
          dw f,250, f,250, f,250, f,250, e,500, e,500
          dw d,250, d,250, d,250, d,250, c,500
          dw 0                  ;Abschluá immer mit 0

oldInt     dd 0                 ;Zeiger auf alten Handler
Zaehler   dw 0                  ;Zaehler, wird einmal pro ms dekrem.
data ends

code segment
assume cs:code,ds:data

handler proc far                ;neuer IRQ 0 - Handler
  pushf
  call dword ptr oldint         ;alten Handler aufrufen
  mov ax,data                   ;Datensegment Zugriff ermglichen
  mov ds,ax
  dec word ptr Zaehler          ;Zaehler dekrementieren
  iret
handler endp

prepare proc near               ;bereitet Timer und Speaker vor
  mov dx,61h                    ;Controll-Port laden
  in al,dx
  or al,3                       ;untere Bits setzen (enable Speaker)
  out dx,al

  mov al,36h                    ;Schreibzugriff Timer 0
  mov cx,04a9h                  ;Interrupt-Abstand 1 ms
  out 43h,al                    ;Befehl senden
  mov al,cl
  out 40h,al                    ;Timer-Wert senden
  mov al,ch
  out 40h,al

  mov ax,3508h                  ;alten Interrupt-Vektor lesen
  int 21h
  mov word ptr oldint,bx        ;Vektor sichern
  mov word ptr oldint+2,es
  push ds
  mov ax,cs                     ;Vektor auf Handler in ds:dx
  mov ds,ax
  lea dx,handler
  mov ax,2508h                  ;und neuen Vektor setzen
  int 21h
  pop ds
  ret
prepare endp

close proc near                 ;setzt Timer und Speaker wieder zurck
  push ds
  lds dx,oldint                 ;alten Vektor restaurieren
  mov ax,2508h
  int 21h

  mov al,36h                    ;Timer zurcksetzen
  out 43h,al
  xor al,al
  out 40h,al                    ;auf 18,2 Interrupts pro Sekunde
  out 40h,al

  mov dx,61h                    ;Speaker aus
  in al,dx
  and al,not 3                  ;(Speaker enable lschen)
  out dx,al
  pop ds
  ret
close endp

delay proc near                 ;wartet (Zeit in ms in ax)
  mov zaehler,ax                ;Zaehler laden
warte:
  cmp zaehler,0                 ;warten, bis Interrupt
  jne warte                     ;Zaehler auf 0 gezhlt hat
  ret
delay endp

sound proc near
  mov bx,ax                     ;Frequenz nach bx
  mov al,0b6h                   ;Timer 2 auf Rechteck programmieren
  out 43h,al
  mov dx,0012h                  ;1.193 MHz Eingangsfrequenz
  mov ax,34ddh
  div bx                        ;Timer-Wert berechnen
  out 42h,al                    ;Low-Byte an Timer 2
  mov al,ah
  out 42h,al                    ;High-Byte an Timer 2
  ret
sound endp

start proc
  mov ax,data                   ;Zugriff auf Datensegment ermglichen
  mov ds,ax

  call prepare                  ;Timer und Speaker initialisieren

  lea si,song                   ;Zeiger auf Frequenzen

weiter:
  lodsw                         ;Frequenz holen
  or ax,ax
  je fertig                     ;Abschluá-Byte gefunden ?
  call sound                    ;Sound ausgeben
  lodsw                         ;Dauer laden
  call delay                    ;und warten
  jmp weiter

fertig:
  call close                    ;Timer und Interrupts zurcksetzen
  mov ah,4ch                    ;Programm beenden
  int 21h
start  endp

code ends
end start
