I am doing an Assembly Calculator, and I need to do a Menu, but my program only prints the first string.
TITLE CALCULADORA
.MODEL SMALL
.STACK 100H
.DATA
;menu
MENU DB 'MENU$'
MSOMA DB 'Digite 1 para Soma$'
MSUB DB 'Digite 2 para Subtração$'
.CODE
MAIN PROC
MOV AX,#DATA ;Localizacao do endereco de memoria das variaveis, e colocando-os en AX
MOV DS,AX ;transferindo as dados da memoria em seu local padrao, no caso, DS (Data Stack)
LEA DX,MENU ;Colocando o endereco do menu em DX
MOV AH,9 ;funçao de exibicao de string
INT 21H ;execucao de AH
CALL PULA_LINHA ;"Chamando" a funcao PULA_LINHA
LEA DX,MSOMA ;Colocando o endereco do menu em DX
MOV AH,9 ;funçao de exibicao de string
INT 21H ;execucao de AH
CALL PULA_LINHA ;"Chamando" a funcao PULA_LINHA
MAIN ENDP
PULA_LINHA PROC
MOV AH,2 ;inicia a funçao de leitura de caracter
MOV DL,0DH ;caracter para o cursor retornar a posicao inicial
INT 21H ;executando o conteudo de AH, que por sua vez printa o conteudo de DL
MOV DL,0AH ;caracter de descer a linha
INT 21H ;executando a tarefa
PULA_LINHA ENDP
MOV AH,4CH ;saida do dos
INT 21H ;saindo
END MAIN
As Michael already mentioned in his comment, you "messed up" the returning functions a bit, and mistaken "return to OS" and "return to Caller":
TITLE CALCULADORA
.MODEL SMALL
.STACK 100H
.DATA
;menu
MENU DB 'MENU$'
MSOMA DB 'Digite 1 para Soma$'
MSUB DB 'Digite 2 para Subtração$'
.CODE
MAIN PROC
MOV AX,#DATA ;Localizacao do endereco de memoria das variaveis, e colocando-os en AX
MOV DS,AX ;transferindo as dados da memoria em seu local padrao, no caso, DS (Data Stack)
LEA DX,MENU ;Colocando o endereco do menu em DX
MOV AH,9 ;funçao de exibicao de string
INT 21H ;execucao de AH
CALL PULA_LINHA ;"Chamando" a funcao PULA_LINHA
LEA DX,MSOMA ;Colocando o endereco do menu em DX
MOV AH,9 ;funçao de exibicao de string
INT 21H ;execucao de AH
CALL PULA_LINHA ;"Chamando" a funcao PULA_LINHA
; ----------------------------------------------------------<<<<
; HERE you want to quit to OS, so the INT 21h/4Ch goes here
MOV AH,4CH ;saida do dos
INT 21H ;saindo
; ----------------------------------------------------------<<<<
MAIN ENDP
PULA_LINHA PROC
MOV AH,2 ;inicia a funçao de leitura de caracter
MOV DL,0DH ;caracter para o cursor retornar a posicao inicial
INT 21H ;executando o conteudo de AH, que por sua vez printa o conteudo de DL
MOV DL,0AH ;caracter de descer a linha
INT 21H ;executando a tarefa
; ----------------------------------------------------------<<<<
; HERE you just want to return to the caller, that's done with
RET
; ----------------------------------------------------------<<<<
PULA_LINHA ENDP
END MAIN
Related
I need help in combining two programs I have and I can't seem to get it working for me. Don't get the desired output.
So here's my problem statement:
Combine Two separate strings in a third string and display it, Where the first String is as it is and the second string is reversed.
Example:
Input:
String 1: 'Hello'
String 2: '.dlroW '
Output:
'Hello World.'
end of Example.
Now there are two ways we can go about this.
First: Use string functions.(Preferred)
Now I am fairly new to learning Assembly Language so I would like to do it using string functions so I can learn something New.
Second: Without using string functions.
Another Approach is if someone can help combining two programs, One for the concatenation of the string and the other for reversal, Note that I have written the two individual programs and they run well without any hiccups, I just can't seem to do it together. How I am going about with this is before concatenating the string I am trying to reverse it, then proceeding with the addition of the second string. But I can't seem to get it working. I've tried to the best of my knowledge.
//Concatenation Code
.model tiny
.data
msg1 db 10,13,"Enter the string 1: $"
cat db 30 DUP('$')
msg2 db 10,13,"Enter the string 2: $"
msg3 db 10,13,"Concatenated string is: $"
.code
mov ax,#data
mov ds,ax
lea dx,msg1
mov ah,09h
int 21h
lea si,cat
up: mov ah,01h
int 21h
mov [si],al
inc si
cmp al,0dh
jnz up
lea dx,msg2
mov ah,09h
int 21h
dec si
up1: mov ah,01h
int 21h
mov [si],al
inc si
cmp al,0dh
jnz up1
lea dx,msg3
mov ah,09h
int 21h
lea dx,cat
mov ah,09h
int 21h
mov ah,4ch
int 21h
end`
Here's Part 2
//Reversal Code
.model tiny
.data
msg1 db 10,13,"enter the string: $"
string db 40 DUP('$')
rev db 40 DUP('$')
msg2 db 10,13,"reverse string is: $"
.code
mov ax,#data
mov ds,ax
lea dx,msg1
mov ah,09h
int 21h
mov ah,0ah
lea dx,string
int 21h
lea si,string
lea di,rev
mov cl,[si+1]
mov ch,00h
add di,cx
inc si
inc si
up: mov al,[si]
mov [di],al
inc si
dec di
loop up
inc di
mov ah,09h
lea dx,msg2
int 21h
mov ah,09h
lea dx,[di]
int 21h
mov ah,4ch
int 21h
end
And Here is the code I came Up with by combining those two.
//That's the code I tried Combining
.model tiny
.data
.model tiny
.data
msg1 db 10,13,"Enter string1: $"
cat db 30 DUP('$')
msg2 db 10,13,"Enter string2: $"
msg3 db 10,13,"Concatenated string is: $"
.code
mov ax, #data
mov ds,ax
lea dx,msg1
mov ah,09h
int 21h
lea si,cat
up: mov ah,01h
int 21h
mov [si],al
inc si
cmp al,0dh
jnz up
lea dx, msg2
mov ah,09h
int 21h
dec si
up2:mov al,[si]
mov [di],al
inc si
dec di
loop up2
inc di
up1:mov ah,01h
int 21h
mov [si],al
inc si
cmp al,0dh
jnz up1
lea dx,msg3
mov ah,09h
int 21h
lea dx,cat
mov ah,09h
int 21h
mov ah,4ch
int 21h
end
My Output
As you can see clearly I have failed at doing either task correctly. So can someone tell me where I am going wrong? Or teach me how to do this using the string Functions?
The up2 loop that tries to do string reversal comes too soon!. You've placed it where the 2nd string (the one that needs reversal) isn't even inputted yet.
If you would have written comments in your program, then you would probably have noticed this yourself.
This up2 loop uses the LOOP instruction that depends on the CX register but your program does not assign any suitable value to CX.
And also your working reversal program is using 2 buffers. Why then do you expect the combo to work from a single buffer?
Define the cat buffer so it can hold both strings.
Define the str buffer so it can hold the second string.
lea dx, msg1
mov ah, 09h ; DOS.PrintString
int 21h
lea di, cat
up: ; Input f i r s t string
mov ah, 01h ; DOS.GetCharacter
int 21h ; -> AL
mov [di], al
inc di
cmp al, 13
jne up
dec di ; Throw out the 13
; This marks the start of the reversed string, VERY IMPORTANT
; So don't change DI while inputting the 2nd string
lea dx, msg2
mov ah, 09h ; DOS.PrintString
int 21h
lea si, str
mov dx, si
up1: ; Input s e c o n d string
mov ah, 01h ; DOS.GetCharacter
int 21h ; -> AL
mov [si], al
inc si
cmp al, 13
jne up1
dec si ; Throw out the 13
cmp si, dx
je done ; Second string was empty. CAN HAPPEN!
up2: ; Reversed copying of s e c o n d string
dec si
mov al, [si]
mov [di], al
inc di
cmp si, dx
ja up2
done:
mov ax, 0A0Dh ; Add a proper carriage return and linefeed to the result
mov [di], ax
mov al, '$' ; Terminate the result with a dollar sign
mov [di+2], al
lea dx, msg3
mov ah, 09h ; DOS.PrintString
int 21h
lea dx, cat
mov ah, 09h ; DOS.PrintString
int 21h
First: Use string functions.(Preferred)
Both in the up loop and in the up2 loop, do you find next pair of instructions:
mov [di], al
inc di
Provided
the direction flag DF is clear so that DI can increment
the ES segment register points to #data
you can replace these 2 instructions by a single STOSB instruction.
This is what needs to go on top of your program:
.code
mov ax, #data
mov ds, ax
mov es, ax
cld
If we allowed ourselves to write a silly sequence of multiple std (set direction flag) and cld (clear direction flag) instructions, we could also replace mov al, [si] with lodsb. Care must be taken to keep a valid SI pointer (*).
dec si ; (*)
up2: ; Reversed copying of s e c o n d string
std
lodsb ; Due to STD, SI will decrement
cld
stosb ; Due to CLD, DI will increment
cmp si, dx
jae up2 ; (*)
done:
mov ax, 0A0Dh ; Add a proper carriage return and linefeed to the result
stosw
mov al, '$' ; Terminate the result with a dollar sign
stosb
In code that sets the direction flag (using std) it is best to end with a cld instruction so the direction flag is in the state we most expect!
I have to create an assembly program using YASM on the i386 architecture (32 bits) that receives a text as a parameter and returns a text with the same text but with each line numbered.
Example:
00 this is what the final text should look like
01 all lines numbered
02 and the last line should have the amount of total lines
03 Total lines:3.
; $ yasm -f elf enum.asm
; $ ld -o enum enum.o
; $ ./fibonacci
%define stdout 1
section .data
file_name db 'test.txt'
new_file db 'resultado.txt'
num db "00: ",4,
numL equ $ - num
bufferEntradaL dd 1
salto db 0xa
section .bss
descriptorEntrada resb 2
bufferEntrada resb 2
descriptorSalida resb 2
descriptorEntrada resb 2
punteroBuffer resb 2
cant resb 2
section .text
global _start
abrirArchivoLectura:
;Abre un archivo
mov EAX, sys_open ; Llamo a sys_open
mov ECX, 0 ; Solo lectura
mov EBX file_name ; Nombre del archivo
int 80h ; Llamo al sistema
ret
abrirArchivoEscritura:
mov EAX, sys_open ; Llamo al sys_open
mov ECX, 1 ; Modo solo escritura
mov EBX new_file ; Nombre del archivo
int 80h ; Llamo al sistema
ret
crearArchivoEscritura:
mov EAX, sys_create
mov EBX new_file
mov ECX, 1
int 80h
ret
leerArchivo:
;Lee un archivo
mov EAX, sys_read ; Llamo a sys_read
mov EBX, [descriptorEntrada] ; Descriptor del archivo
mov ECX, bufferEntrada ; Buffer de entrada
mov EDX, bufferEntradaL ; Tamaño del buffer
int 80h ; Llamo al sistema
ret
imprimirMensaje:
;Imprime un mensaje de ayuda
mov EAX, sys_write ; Llamo a sys_write
mov EBX, stdout ; Imprimir por pantalla
mov ECX, num ; Mensaje a imprimir
mov EDX, numL ; Longitud
int 0x80 ; Llamo al sistema
jmp salirSinError ; Sale sin error
imprimirSaltoPantalla:
;Imprime un salto de linea por pantalla
mov EAX, sys_write ; Llamo a sys_write
mov EBX, stdout ; Imprimir por pantalla
mov ECX, salto ; Mensaje a imprimir
mov EDX, 1 ; Longitud
int 0x80 ; Llamo al sistema
ret
cerrarArchivoEntrada:
;Cierra el archivo de entrada
mov EAX, sys_close ; Llamo a sys_close
mov EBX, [descriptorEntrada] ; Muevo el descriptor de salida al registro EBX
int 80h ; Llamo al sistema
ret
cerrarArchivoSalida:
;Cierra el archivo de salida
mov EAX, sys_close ; Llamo a sys_close
mov EBX, [descriptorSalida] ; Muevo el descriptor de salida al registro EBX
int 80h ; Llamo al sistema
ret
leerHastaSaltoLinea:
mov [punteroBuffer],ECX ; Le asigna a la variable punteroBuffer el contenido del registro ECX
mov [cant],EAX ; Le asigna a la variable cant el contenido del registro EAX
cmp cant,salto
jne leerHastaSaltoLinea
loop:
_start:
;Comienza el programa
call
call abrirArchivoLectura ; Abre el archivo de entrada
test EAX,EAX ; Testea que el parametro ingresado por el usuario sea un archivo.txt
js salirErrorArchivoEntrada ; Si no es un archivo.txt sale con un error de archivo de entrada
mov [descriptorEntrada],EAX ; Guardo el descriptor del archivo de entrada
call leerArchivo ; Lee el archivo de salida
call leerHastaSaltoLinea
salirErrorArchivoEntrada:
;Salir con error en archivo de entrada
mov EAX,sys_exit ; Llamo a sys_exit
mov EBX, 2 ; Finalizo por error en el archivo de entrada
int 0x80 ; Llamo al sistema
One obvious algorithm if you're just using read/write system calls directly is to allocate a 2nd buffer (large enough to hold the result even if every byte of input is a newline). Pages that you never touch don't really count as used, so it's fine to have a very large BSS.
In a loop:
do {
format the current line-number counter as a string, into the current position of the output buffer, plus a trailing space or tab. (NASM/YASM example How do I print an integer in Assembly Level Programming without printf from the c library? easy to port from x86-64 to i386.) But it would be more efficient to avoid re-doing the div-by-10 stuff every time, and just increment the least-significant digit until it's > '9', then redo the formatting.)
copy bytes from the input buffer up to and including the next newline ('\n' = 0xa. YASM doesn't support NASM's backtick-string / character literals that process C-style escape sequences). Also make sure you stop at the end of the buffer if it doesn't end with a newline. (You could check for this before the loop and append one if there isn't one, so simplifying your loop).
} while(input_pos < end_of_input)
When you're done, find the length of the result by subtracting the current position from the start of the buffer, and use that for a sys_write.
If you want to support files larger than your input/output buffer, remember whether you were at the end of a line or not when looping back to do another sys_read. (Instead of actually copying that partial line back to the beginning of the input buffer. The copying strategy would fail with a line longer than your buffer size.)
Don't forget that sys_read and sys_write can return early, having read or written fewer bytes than you asked, even if there are more bytes to read/write. (e.g. if you're reading from a pipe, including a named pipe, or if a signal came in during the system call). IIRC, the libc wrapper functions might handle retrying.
An alternative might be to scan through counting newlines, then work from the end of the buffer to expand it in-place. This would be slightly more cache-efficient, but it has the downside of needing an initial scan to count newlines to figure out how much space you'll need to leave (and what line number to count down from).
My first suggestion has the advantage that you only touch each input character once, instead of twice for this one. If you use a small-ish buffer that fits in L1D cache (like 16kiB), then expanding it in-place from the end might be worth considering, but it's more complex.
OTOH, if you're really trying to optimize for efficiency, you could maybe use vmsplice(2) to "gift" some pages to the kernel, into a temporary pipe and from there splice them into a regular file. So the physical pages you wrote end up as part of the pagecache with zero-copy. This might have more overhead than just making write() system calls, though.
Both of the previous methods have the advantage of only making one large write() system call for a whole buffer. It would also be possible to make an inefficient program that copies line-number + a line into a tmp buffer and uses sys_write on that, or even worse sys_write the line number text and then sys_write the line in-place in the input buffer.
IDK if that's any easier to implement, because you still have to get all the byte-counts right, and it sucks for performance. A sys_write is pretty slow compared to copying a few bytes.
I was wondering i someone could help me with my code, i want to read more than one time from the stdin in x86 but when i read the second time, it is ignoring the number and just add the 50. I want it to work so that i can call it as many times as i need to go adding the register EAX by an arbitrary number (which i will load from variables A,B,etc later on)
section .text
global _start
_start:
call leer
call atoi
add eax,10
call itoa
call imprimir
call limpiar
call leer
call atoi
add eax,50
call itoa
call imprimir
jmp salir
;-----------ATOI
atoi:
mov esi,Buffer ;move buffer address
mov eax,0 ;where im going to keep result
mov ebx,0 ;where i put char
.atoi_start:
mov bl, byte[esi] ;get the char
je .end_atoi
cmp bl, '0' ;check if null
jb .end_atoi
cmp bl,'9'
ja .end_atoi
imul eax,10 ;multiplico resultado por 10
sub bl,0x30 ;ascii->int
add eax,ebx ;agegue el nuevo digito
inc esi ;getting ready for next char
jmp .atoi_start
.end_atoi:
ret ;at this point i have int representation in eax
;-----------ITOA
itoa:
mov ebx, eax ;mueve el numero en eax a ebx
mov esi, Buffer
mov ebx,10
add esi,10
.itoa2:
xor edx,edx
div ebx
add dl,'0' ;lo convierte en char
mov [esi],dl
dec esi
test eax,eax
jnz .itoa2
jz .doneItoa
.doneItoa:
ret
;----------------------LIMPIAR
limpiar:
;limpia buffer usando eax
xor eax,eax;
xor edx,edx
xor ecx,ecx
mov ecx, 1100 ;tamano de veces que voy a limpiar
lea edx, [Buffer] ;direccion inicial del buffer
.loop:
mov [edx+ecx],eax ;voy a limpiar de atras para adelante
dec ecx ;decremento contador y verifico si es 0 para seguir limiando
jnz .loop
ret
imprimir:
mov eax,4
mov ebx,1
mov ecx,Buffer
mov edx,1100 ;tamano
int 0x80 ;syscall
;ahora imprimo nueva linea
mov eax,4
mov ebx,1
mov ecx,nuevaLinea
mov edx,1
int 0x80
ret
leer:
;vamos a poner el msg en pantalla
mov eax,4 ;sys_write
mov ebx,1 ;stdout
mov ecx,msg ;paso el mensaje
mov edx,lenMsg ;paso el largo del mensaje
int 80h
;ahora leemos
mov eax,3 ;sys_read
mov ebx,2 ;stdin
mov ecx,Buffer
mov edx,1100
int 80h
ret
salir:
mov rax,60 ;sys_exit
mov rdi,0 ;codigo de salida
syscall
section .data
msg db 'Ingrese un numero: '
lenMsg equ $-msg
nuevaLinea db 10;nueva linea
A dq 0
section .bss
Buffer: resb 1100
B: resb 1100
In atoi, the fifth instruction
je .end_atoi
tests the Z flag which has not been set in this function. So it is jumping based on the value of Z from whatever happened before atoi is called, which is the read system call in leer.
Either put
test bl, bl
before that je instruction or just remove the je, since the end of string will be caught by the cmp '0' that follows.
But that's not a complete fix: The read system call doesn't null terminate the input. Leer should use the number of bytes read (returned by the read system call) to place a null byte in the buffer after the input.
What I want to do is to read a string from the keyboard and output that same string. However, using the code below in TASM, I get only gibberish:
UPDATED
DATA SEGMENT PARA PUBLIC 'DATA'
MSG DB 10,0,80 dup(?) ; variable to hold string
DATA ENDS
CODE SEGMENT PARA PUBLIC 'CODE'
START PROC FAR
ASSUME CS:CODE, DS:DATA
PUSH DS
XOR AX, AX
PUSH AX
MOV AX,DATA
MOV DS, AX
MOV AH, 0AH
MOV DX, OFFSET MSG
INT 21H ; read string
MOV AH, 09H
INT 21H ;output string
RET
START ENDP
CODE ENDS
END START
Now I get the chance to enter input but the result is gibberish. Where am I wrong?
Thanks to all comments, I managed to write the program which reads and displays a string:
DATA SEGMENT PARA PUBLIC 'DATA'
MAXLEN DB 20
LEN DB 0
MSG DB 20 DUP(?)
DATA ENDS
CODE SEGMENT PARA PUBLIC 'CODE'
START PROC FAR
ASSUME CS:CODE, DS:DATA
PUSH DS
XOR AX, AX
PUSH AX
MOV AX,DATA
MOV DS, AX
MOV AH, 0AH
MOV DX, OFFSET MAXLEN
INT 21H
MOV DL, 10
MOV AH, 02H
INT 21H ;NEW LINE FEED
MOV AL, LEN
CBW ; EXTEND AL TO AX
MOV SI, AX
MOV MSG+SI, '$'
MOV AH, 09H
MOV DX, OFFSET MSG
INT 21H
RET
START ENDP
CODE ENDS
END START
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!
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.