; ASCII Organ III

timer   equ     42h
speaker equ     61h
sponmsk equ     4fh
spofmsk equ     4ch
settim  equ     0b6h
keyport equ     60h

doscall macro   x
        mov     ah,x
        int     21h
        endm

pout    macro   x,y
        mov     al,y
        out     x,al
        endm

code    segment
        org     100h
        assume  cs:code,ds:code

start:  jmp     begin           ; bypass variable storage

;----- Music data -----

song    db      17,4,16,4,15,4,16,4,17,4,17,4,17,8
        db      16,4,16,4,16,8,17,4,19,4,19,8
        db      17,4,16,4,15,4,16,4,17,4,17,4,17,4
        db      17,4,16,4,16,4,17,4,16,4,15,4

;----- Timer divisor data (Pitch) ------

;               C-1  D-2  E-3  F-4  G-5  A-6  B-7  C-8  D-9  E-10 F-11
divsors dw 0000,9121,8126,7239,6833,6088,5423,4832,4560,4063,3620,3417
;               G-12 A-13 B-14 C-15 D-16 E-17 F-18 G-19 A-20 B-21 C-22
        dw      3044,2712,2416,2280,2031,1810,1708,1522,1356,1208,1140
;               D-23 E-24 F-25 G-26 A-27 B-28
        dw      1016, 905, 854, 761, 678, 604

;----- Variables ------

notecnt dw      0                               ; counter for notes played
beatcnt dw      0                               ; counter for beats
tbtn    dw      0                               ; n beats

;----- Initialize ------

begin:  mov     al, [song+1]                    ; length of first note
        cbw
        mov     tbtn,ax
        call    playnot                         ; begin first note
        mov     dx,offset target1c
        mov     al,1ch                          ; get timer interrupt
        doscall 25h
        mov     dx,offset lbyte+1
        int     27h                             ; end, make resident

;----- target for interrupt 1Ch ------

target1c:
        push    cs                      ; synchronize execution time
        pop     ds     
        inc     beatcnt 
        mov     ax,tbtn     
        cmp     ax,beatcnt              ; end of note?
        je      in2                     ; yes, jump next note
        iret                            ; no...
in2:    cmp     notecnt,(offset divsors - offset song) / 2
        jne     in3                     ; if not equal continue
        mov     byte ptr target1c,0cfh
        iret

; to play following note

in3:    push    si                      ; store ds,dx,ax
        inc     notecnt                 ; advance to next note
        mov     beatcnt,0               ; reset beatcount=0
        mov     si,notecnt
        shl     si,1                    ; 2 elements for each note
        mov     al,[si+offset song+1]   ; duration of the note
        cbw
        mov     tbtn,ax
        call    playnot
        pop     si
        iret

playnot: mov     si,notecnt
        shl     si,1
        mov     al,[si+offset song]     ; AL = number of note
        or      al,al                   ; force pointers
        jnz     pn1                     ; note 0 is a pause
        pout    speaker,spofmsk
        ret
pn1:    cbw                             ; AH in one byte
        mov     si,ax
        shl     si,1                    ; divisor table
        pout    timer+1,settim
        mov     ax,[si+offset divsors]
        out     timer,al
        mov     al,ah
        out     timer,al
        pout    speaker,sponmsk
lbyte:  ret

code    ends
        end     start
