0

I'm in an introductory assembly class, and for the final project we have to make a program that plays Happy Birthday.

The problem is that the program plays the song half way through (to the 13th note), then after that stops. I've tried everything to try and fix it and I have no idea why it shouldn't be working. I've tried changing the code around, changing the way the loop works, and changing which registers hold different values, but nothing seems to be working. I looked through debug and I can't see anything out of the ordinary. This is the code for the program:

;data segment

dseg      segment para 'data'
    ;hex value for each note to send to port 42
    NOTES DW 11CAh,11CAh,0FDAh,11CAh,0D5Ah,0E1Fh,11CAh,11CAh
          DW 0FDAh,11CAh,0BE3h,0D5Ah,11CAh,11CAh,08E9h,0A97h
          DW 0D5Ah,0E1Fh,0FDAh,0A00h,0A00h,0A97h,0D5Ah,0BE3h,0D5Ah,"$"

    ;duration for each note
    DUR DB 1d,1d,2d,2d,2d,4d,1d,1d,2d,2d,2d
        DB 4d,1d,1d,2d,2d,2d,2d,6d,1d,1d,2d,2d,2d,4d    

dseg      ends

;----------------------------------------------------------------------
;code segment
cseg      segment para 'code'

main    proc far               ;this is the program entry point
    assume cs:cseg, ds:dseg, ss:sseg 
    mov  ax,dseg           ;load the data segment value
    mov  ds,ax             ;assign value to ds

    MOV AL, 0B6h
    OUT 43h, AL     ;output whats in AL to port 43h
    MOV SI, OFFSET NOTES    ;moves the address of the first note into SI
    MOV DI, OFFSET DUR  ;moves the address of the first duration into DI
    SUB BX, BX      ;clears BX
    SUB DX, DX      ;clears DX
    SUB CX, CX      ;cleasr CX
PLAY:   NOP
    MOV DX, [SI]        ;moves the frequency and divider for the note into DX
    CMP DX, "$"     
    JE EXIT         ;if at end of notes jump to exit
    MOV BL, [DI]        ;moves the number of times delay is called into BL
    CALL TONE       ;plays the note for the duration
    INC SI          ;move to next note
    INC DI          ;moves to next duration
    JMP PLAY        ;loop back to PLAY if there are still notes
EXIT:   NOP


      mov  ah,4Ch            ;set up interupt
      int  21h               ;Interupt to return to DOS

main      endp


TONE PROC 

    SUB AX, AX      ;clears ax
    MOV AL, DL      ;sets up the frequency for the tone
    OUT 42h, AL     ;output whats in AL to port 42h
    MOV DL, 0       ;use to compare to BL
    MOV AL, DH      ;divider for the tone smaller value = higher pitch
    OUT 42h, AL
    IN AL, 61h      ;input the contents of port 61h into AL
    MOV AH, AL      ;preserve contents of AL
    OR AL, 00000011b    ;uses mask to change the bits that we need
    OUT 61h, AL     ;outputs the changed bits back into the port to start tone
AGAIN:  NOP         ;loops 1/2 second delay for times needed
    CALL DELAY
    DEC BL
    CMP BL, DL      ;compares value in bl to 0
    JNE AGAIN       ;loops delay until bl is 0
    MOV AL, AH
    OUT 61h, AL     ;turns off tone with preserved contents from port 61h
    CALL DELAY_TWO      ;adds delay between notes
    RET

TONE ENDP

DELAY PROC NEAR     ;half second delay

    PUSH AX     ;preserve value in ax in stack
    MOV CX, 33156   ;delays for half second
WAITF:  NOP
    IN AL, 61h  ;read port 61
    AND AL, 10h ;gives 0001 0000 check PB4
    CMP AL, AH
    JE WAITF    ;jump to WAITF if AL is equal to AH
    MOV AH, AL  ;makes both AL and AH contain same number
    LOOP WAITF  ;continue until CX is 0
    POP AX  
    RET

DELAY ENDP

DELAY_TWO PROC NEAR ;small delay between notes

    PUSH AX     ;need value in ax for TONE so save it
    MOV CX, 331
WAITO:  NOP
    IN AL, 61h
    AND AL, 10h
    CMP AL, AH
    JE WAITO
    MOV AH, AL
    LOOP WAITO
    POP AX      ;get back ax value once delay is done
    RET

DELAY_TWO ENDP


cseg      ends
      end     main           ;Program exit point

We're only using 16 bit assembly. There's probably other methods for a timer and making tones but we have to do it this way, with the ports used in the program. Also, I'm using DOSBox to run the .exe and it sounds a little off when it plays.

Any help would be greatly appreciated.

Bernandion
  • 69
  • 1
  • 3
  • 2
    You need to increment SI twice for each note, since it points an array of 16-bit values. – Ross Ridge Apr 05 '16 at 03:32
  • 1
    Thank you so much! I forgot that I needed to do that for 16 bit values. It works perfectly now. – Bernandion Apr 05 '16 at 03:38
  • 1
    The standard idiom for zeroing a register is `xor same,same`. IIRC, on a few CPUs, `sub same,same` doesn't have [the same performance advantages as `xor`](http://stackoverflow.com/questions/33666617/which-is-best-way-to-set-a-register-to-zero-in-x86-assembly-xor-mov-or-and). – Peter Cordes Apr 05 '16 at 13:27

0 Answers0