How to play chords in ASM 8086?

2020-03-30 06:06发布

问题:

I want to know what is the best way to play more then 1 note at the time in assembly. If you can, please add a procedure that explain your answer. Thanks!

回答1:

Orange, next code is a program a made long time ago in EMU8086 and Windows XP (it ran at that time). Now I have Windows 8 64 bits and it doesn't run anymore. I will give you the code because it may help you. All the names and comments are in spanish because I am costarrican, but the assembler code is universal (google translator will give you a hand):

.model small
.stack 100h
.data

;----------------------------------------------------------------------------

MENSAJEPLAY    DB '                       PROYECTO ® PIANO ¯',13,10
               DB 13,10
               DB '            LAS TECLAS DE LAS NOTAS VAN EN EL ORDEN SIGUIENTE:',13,10
               DB 13,10
               DB '                        2  3     5  6  7' ,13,10
               DB '                      q  w  e  r  t  y  u',13,10,13,10
               DB 13,10
               DB '                        s  d     g  h  j' ,13,10
               DB '                      z  x  c  v  b  n  m',13,10,13,10
               DB '                       PARA TERMINAR PRESIONE ESC','$'

;----------------------------------------------------------------------------

.code

TONO MACRO NUMERO               ;Esta macro recibe el tono
        MOV     BX,NUMERO       ;y manda a llamar a los procedimientos
        CALL    BOCINA
ENDM

;----------------------------------------------------------------------------

CLRSCR PROC
;Limpia la pantalla
        MOV     AH,6
        XOR     AL,AL
        XOR     CX,CX
        MOV     DX,184FH
        MOV     BH,13
        INT     10H
        RET
ENDP

;----------------------------------------------------------------------------

BocinaOn  PROC                  ;Activa la bocina
        IN      AL, 61h
        OR      AL, 11B
        OUT     61h, AL
        RET
BocinaOn  ENDP

;----------------------------------------------------------------------------

BocinaOff  PROC                 ;Desactiva la bocina
        IN      AL, 61h
        AND     AL, 11111100b
        OUT     61h, AL
        RET
BocinaOff  ENDP

;----------------------------------------------------------------------------

Ajustar  PROC                  ;Ajusta la bocina con la frecuencia dada
        PUSH    BP
        MOV     BP, SP
        MOV     DX, 18      
        MOV     AX, 13353   
        MOV     BX, [BP + 4]
        DIV     BX
        MOV     BX, AX  
        MOV     AL, 0B6h
        OUT     43h, AL
;ENVIAR AL PUERTO LA FRECUENCIA EN DOS BYTES POR SEPARADO.
        MOV     AX, BX
        OUT     42h, AL ;ENVIA PRIMER BYTE. (PUERTO PARALELO = 378H)
        MOV     AL, AH
        OUT     42h, AL ;ENVIA SEGUNDO BYTE. (PUERTO SERIAL = 3F8H)
        POP     BP
        RET
Ajustar  ENDP

;----------------------------------------------------------------------------

Suena proc                      ;Activa la bocina y coloca el nombre de
        CALL bocinaON           ;la tecla.
        MOV     AX,40H
        MOV     ES,AX
        MOV     DX,ES:[006EH]
        MOV     AX,ES:[006CH]
        ADD     AX,7
        ADC     DX,0            ;Se le suma 7 unidades a ese valor
CLIC:
        CMP     DX,ES:[006EH]   ;Y se compara hasta que sean iguales
        JB      FINI            ;Pasando por un ciclo, cuando llegen
        JA      CLIC            ;a ser iguales se sale del ciclo y
        CMP     AX,ES:[006CH]
        JA      CLIC
FINI:
        CALL    BocinaOff       ;Se desconecta la bocina y regresa.
        RET
Suena endp

;----------------------------------------------------------------------------

Bocina proc                     ;Este procedimiento guarda AX y BX en
        PUSH    BX              ;la pila para no perder su valor, con
        MOV     AX, BX          ;esto llama a ajusta y a suena
        PUSH    AX
        CALL    Ajustar         ;Pone la frecuencia en el puerto.
        POP     AX
        POP     BX
        CALL    SUENA           ;Activa el speaker y lo desactiva.
        ret
Bocina endp

;----------------------------------------------------------------------------
;CONVERTIR A MINUSCULA SI ERA MAYUSCULA

MINUSCULA PROC
        CMP AL, 65    ;'A'
        JB  CONTINUAR ;SI LA TECLA ES MENOR QUE LA 'A' NO HACE NADA
        CMP AL, 90    ;'Z'
        JA  CONTINUAR ;SI LA TECLA ES MAYOR QUE LA 'Z' NO HACE NADA
        ADD AL, 32    ;Convierte may£scula en min£scula.
     CONTINUAR:
        RET
MINUSCULA ENDP

;----------------------------------------------------------------------------
;CAPTURA LA TECLA CON LA NOTA QUE EL USUARIO DESEA.

TECLA PROC
        MOV     AH,8            ;Si la hay, obtiene la nota
        INT     21H
        CALL    MINUSCULA
        RET
TECLA ENDP
;----------------------------------------------------------------------------
;Cicla el programa hasta que el usuario presione la tecla ESC.
;El procedimiento reacciona a las teclas indicadas en el segmento de datos.
;Cualquier otra tecla es ignorada.
;La tecla presionada es convertida a min£scula, ya que la tabla ASCII
;trata distinto unas de otras.
;Despu‚s de que cada tecla es presionada, el ciclo vuelve al inicio y
;se repite.
;Si la tecla presionada corresponde a una nota musical, el c¢digo
;correspondiente es enviado al parlante.

SPEAKER PROC
COMIENZA:
        CALL    TECLA
        CMP     AL,'q'   ;DO alto
        JNE     S1       ;SI NO ES LA TECLA ESPERADA, SALTA PARA VERIFICAR LA SIGUIENTE.
        TONO    523      ;SI ES LA TECLA ESPERADA, GENERA EL SONIDO CORRESPONDIENTE
        JMP     COMIENZA ;DESPUES DEL SONIDO REINICIA PARA ESPERAR OTRO SONIDO.
S1:     CMP     AL,'w'   ;RE alto
        JNE     S2
        TONO    587
        JMP     COMIENZA
S2:     CMP     AL,'e'   ;MI alto
        JNE     S3
        TONO    659
        JMP     COMIENZA
S3:     CMP     AL,'r'   ;FA alto
        JNE     S4
        TONO    698
        JMP     COMIENZA
S4:     CMP     AL,'t'   ;SOL alto
        JNE     S5
        TONO    784
        JMP     COMIENZA
S5:     CMP     AL,'y'   ;LA alto
        JNE     S6
        TONO    880
        JMP     COMIENZA
S6:     CMP     AL,'u'   ;SI alto
        JNE     S8
        TONO    988
        JMP     NOSALTO1
SALTO1:
   JMP COMIENZA
NOSALTO1:
        JMP     COMIENZA
S8:     CMP     AL,'2'   ;DO# alto
        JNE     S9
        TONO    554
        JMP     COMIENZA
S9:     CMP     AL,'3'   ;RE# alto
        JNE     S10
        TONO    622
        JMP     COMIENZA
S10:    CMP     AL,'5'   ;FA# alto
        JNE     S11
        TONO    740
        JMP     COMIENZA
S11:    CMP     AL,'6'   ;SOL# alto
        JNE     S12
        TONO    830
        JMP     COMIENZA
S12:    CMP     AL,'7'   ;SIb alto
        JNE     S13
        TONO    923
        JMP     COMIENZA
S13:    CMP     AL,'z'   ;DO bajo
        JNE     S14
        TONO    261
        JMP     COMIENZA
S14:    CMP     AL,'x'   ;RE bajo
        JNE     S15
        TONO    293
        JMP     COMIENZA
S15:    CMP     AL,'c'   ;MI bajo
        JNE     S16
        TONO    329
        JMP     NOSALTO2
SALTO2:
   JMP SALTO1
NOSALTO2:
        JMP     COMIENZA
S16:    CMP     AL,'v'   ;FA bajo
        JNE     S17
        TONO    349
        JMP     COMIENZA
S17:    CMP     AL,'b'   ;SOL bajo
        JNE     S18
        TONO    392
        JMP     COMIENZA
S18:    CMP     AL,'n'   ;LA bajo
        JNE     S19
        TONO    466
        JMP     COMIENZA
S19:    CMP     AL,'m'   ;SI bajo
        JNE     S20
        TONO    498
        JMP     COMIENZA
S20:    CMP     AL,'s'   ;DO# bajo
        JNE     S21
        TONO    277
        JMP     COMIENZA
S21:    CMP     AL,'d'   ;RE# bajo
        JNE     S22
        TONO    311
        JMP     COMIENZA
S22:    CMP     AL,'g'   ;FA# bajo
        JNE     S23
        TONO    370
        JMP     COMIENZA
S23:    CMP     AL,'h'   ;SOL# bajo
        JNE     S24
        TONO    415
        JMP     COMIENZA
S24:    CMP     AL,'j'   ;SIb bajo
        JNE     S25
        TONO    515
        JMP     COMIENZA
S25:    CMP     AL,27 ;27 = tecla ESC (terminar).
        JNE     SALTO2
        RET
SPEAKER ENDP

;----------------------------------------------------------------------------

MENSAJE PROC
        MOV     AH,9
        LEA     DX,MENSAJEPLAY
        INT     21H
        RET
MENSAJE ENDP

;----------------------------------------------------------------------------

EMPIEZA:
        MOV     AX, @data         ;se mandan llamar todos los
        MOV     DS, AX            ;procedimientos
        CALL    CLRSCR            ;Limpiar pantalla.
        CALL    MENSAJE           ;Despliega la explicaci¢n del programa.
        CALL    SPEAKER           ;Sonidos.
        MOV     AX, 4C00H
        INT     21H

;----------------------------------------------------------------------------

END EMPIEZA

What it does is to show you a the keys to press in order to make the notes to sound. You can open it in EMU8086 and run it, but the speaker interrupts behave weird because of Windows 8.

Orange just edited the question, now he wants simultaneous notes playing. Well, my code doesn't do that, it plays one note at a time. For two or more notes to play at the same time we would requiere threads or processes executing that way.

I am not sure assembler can do that, not even sure if the speaker, controlled by an interruption, is allowed to do it. This is because something called "reentrancy", or something like that, it refers to the problem caused when an interrupt is executed when it is already executing, usually the program halts when it happens.