Vistas de página en total

jueves, 4 de julio de 2013

CONCATENAR CADENAS


    Esta función completa las funciones de manejo de cadenas. En este caso concatena dos cadenas.
    En este caso vamos a sumar dos cadenas y las vamos a trasladar a una nueva.
    Los parámetros se pasan por pila. En este programa usaré la versión para ASM. En TASM 2.0 podremos substituir [bp].fuente1 por fuente1 usando el comando user . 

    Ver el paso a TASM en http://myassembler.blogspot.com/2013/08/pasamos-integrar-tasm-20.html

STRCAT.ASM
; Copyright (C) 2013  José Ángel Moneo Fernández

;    This program is free software: you can redistribute it and/or modify
;    it under the terms of the GNU General Public License as published by
;    the Free Software Foundation, either version 3 of the License, or
;   (at your option) any later version.

;    This program is distributed in the hope that it will be useful,
;    but WITHOUT ANY WARRANTY; without even the implied warranty of
;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;    GNU General Public License for more details.

;    You should have received a copy of the GNU General Public License
;    along with this program.  If not, see <http://www.gnu.org/licenses/>.

include main.mac
extrn _strcpy:near
extrn _strlen:near
_modelo exe

_code
rutina _strcat  dest,fuent1,fuent2
        push bx                                           ;Salva registro usado en el programa
        mov ax,[bp].fuent1                       ;toma los parámetros pasados por pila
        push ax                                           ;lo guarda como fuente para la 1ª  llamada a strcpy
        mov ax,[bp].dest                            ;toma los parámetros pasados por pila
        push ax                                           ;lo guarda como destino para la 1ª  llamada a strcpy
        call _strcpy                                     ; llama a copia de string
        pop ax                                             ;recupera la dirección destino de pila
        pop bx                                             ;elimina el otro dato de pila
        mov bx,ax                                       ;guarda la dirección del destino para luego en bx
        push ax                                           ; lo almacena en piula para strlen
        call _strlen                                        ;calcula la longitud
        add sp,2                                             ;elimina los datos de pila
        add bx,ax                                         ;suma al offset de destino, la longitud de destino
        mov ax,[bp].fuent2                         ;Toma la dirección del segundo string 
        push ax                                              ;lo almacena en pila para llamada a strcpy
        push bx                                            ; almacena el offset de destino, que ahora es nuevo
        call _strcpy                                         ;llama a copia de string
        add sp,4                                            ;recupera la pila
        pop bx                                               ;recupera el registro bx
rutina_end    
_data
_end

18 comentarios:

  1. DADO QUE EXISTEN DOS ARREGLOS NUM1 Y NUM2 LOS CUALES SE DEBEN CARGAR Y LUEGO CONCATENARLOS DE MANERA INVERSA EN UN TERCER ARREGLO NUM3 EJEMPLO
    NUM1 1425
    NUM2 123
    NUM3 5421321

    ResponderEliminar
    Respuestas
    1. Ya dispones de la solución en
      http://myassembler.blogspot.com.es/2013/09/problema-planteado-por-lector.html

      Eliminar
  2. Esta función está pensada para utilizarla para componer frases, Es por lo tanto un standar. Lo que se plantea aquí es un problema de clase, que dificilmente usaremos como función real. Pero que es muy sencillo de realizar. Basta con crear una función que invierta una cadena. Aplicamos esta función y luego las unimos con "strcat".

    Por lo tanto crearemos una función "invstring" y aplicaremos la secuencia principal, una vez creadas las macros de llamada a las funciones
    invstring num1
    invstring num2
    strcat mum3,num1,num2

    Así si nos pidiesen realizar num3 como 3215421 bastaría con decir strcat mum3,num2,num1

    Dado que parece que me lo plantean como problema, lo colocaré en una entrada separada, como un ejemplo de programación.

    ResponderEliminar
  3. Ya dispones de la solución en
    http://myassembler.blogspot.com.es/2013/09/problema-planteado-por-lector.html

    ResponderEliminar
  4. Como puedo mostrar la longitud de la cadena en pantalla?

    ResponderEliminar
    Respuestas
    1. Buenos días Carlos:
      Gracias por intereserte por mi blog. Perdona que no te haya contestado antes, pero no me ha llegado el mensaje.
      la respuesta es sencilla. Basta contar los caracteres de la cadena, y luego imprimir el valor que de.
      in icalmente parece complego si se piensa y se quiere resolver de forma directa con un programa, Pero, resulta muy sencillo si se estructura el programa correctamente. por ello en este blog he ido creando rutinas, para que luego puedan ser usadas de forma independiente según las necesidades, como si fuera un lenguaje de alto nivel.
      las funciones puedes verlas en las resto de las entradas, y elegirla a traves de las etiquetas que tengo a la derecha en el blog.
      Por lo tanto, para leer la cadena usas la unficón 'gets'. Esta función te crea una cadena terminada en 0 en un buffer, con los caracteres pulsados por teclado. La función 'strlen' te dará su longitud y te pasará el dato en AX. por lo que solo falta que lo imprimas en pantalla. Como el dato es un número, basta con pasar AX a una variable, para poder usar la función 'printf' la cual te imprime valor numérico en pantalla, en el sistema de numeración que desees.
      Si lo que quires es hacer tu propia versión reducida, en la que no tengas en cuenta todas las opciones que tiene misrutinas, solo tendrás que quedarte con el trozo específico que necesites.
      Yo te pondré el programa, usando mis funciones. la funciones auxiliares gets, printf y strlen, puedes ponerlas directamente en el módulo principal, o tenerlas ya compiladas y guardadas en una librería, y solo usarlas al linkar.
      Dado que esa es la forma mas elegante, ya que te permite crarte la librería para usarlas en futuras tareas, yo pondré el módulo principal, el cual se compila por separado y luego se linka asociandolo a la librería como te explico en la pestaña "Compilación"

      Eliminar
    2. La solución la tienes en forma de programa en la entrada http://www.blogger.com/blogger.g?blogID=5951641172111326221#editor/target=post;postID=6589480377861537959;onPublishedMenu=posts;onClosedMenu=posts;postNum=0;src=postname

      Eliminar
  5. Saludos desde Venezuela!
    Tengo una duda y quisiera saber si podrias ayudarme, en este ejercicio en ASSEMBLER.

    como puedo concatenar dos o mas string, en un string destino. que recibe como entrada dos o mas strign (string1, strign2, ... etc), y que debe producir como salida, todos los string concatenados en un solo string resultante.

    De antemano muchas gracias :D

    ResponderEliminar
    Respuestas
    1. Saludos Anaiviv.
      La función que pongo en esta entrada del blog por si sola ya une dos cadenas, la fuente1 y la fuente2 en una en un string destino. Para ello, como ves, pasamos los punteros de cada cadena a través de la pila, mediante [bp].fuent1. He explicado más pormenorizadamente esta entrada, para que se entienda mejor el programa. Si deseas hacerlo aprovechando el TASM 2.0, tienes ahora en la entrada del blog un enlace a una entrada en que explico las difeencias entre este modelo y el TASM2.0.
      Por lo tanto, para pasar dos cadenas a una tercera basta con el programa tal y como está. Si queremos que sean tres, podríamos hacer un programa repitiendo las llamadas al strlen y al segundo strcpy. Pero resulta mejor y más elegante hacer una macro que llame de forma consecutiva a esta función tantas veces como parámetros añadamos.
      Esta forma de hacerlo la pongo en http://myassembler.blogspot.com/2013/12/concatenar-un-numero-indeterminado.html
      Esto nos permite hacer un polimorfismo con la función, al tener varias formas de llamarla, según las necesidades.
      Como ves no creo nada, aprovecho lo que ya tengo.

      Eliminar
  6. Buenas Tardes Saludos desde Venezuela.
    Mi problema es el siguiente, llamo a una funcion escrita en C++ que altera el valor de un numero pasado por referencia (un puntero a la variable).
    Luego de matarme para llamar a la DLL con invoke lo logre, pero cuando trataba de mostrar el resultado invocando a MessageBox en vez de aparecerme un número me aparecia un caracter todo extraño, creo que es porque el numero lo interpretaba como ASCII.
    Descubri una macro que invocaba la funcion de una dll para hacer eso (llamada udword$), le pasa el valor a eax y lo puedo mostrar en el MessageBox
    pero no puedo guardar el valor contenido en eax, en una variable buffer para tenerlo siempre en modo texto. Quisiera saber como hacer eso. O un macro que pase números a un buffer de texto. Gracias de antemano, te paso parte del código para que medio entiendas:
    .data
    Variable DWORD 15
    .data ?
    buffer db 20 dup(?)
    Res DWORD ?
    ....
    .if eax==IDC_BUTTON1
    invoke SumePorReferencia, Variable, 5
    mov Res, eax
    mov eax, udword$(Res)
    invoke MessageBox, hWin, eax, addr Titulo, MB_OK
    .end if

    Todo va bien hasta ahi - El código funciona y muestra el valor

    Pero si trato de guardar el valor de eax en buffer no puedo.

    mov buffer, eax
    lea buffer, eax

    ninguno de los dos funciona :(
    Te agradezco una solución.
    :D

    PD: El ensamblador que utilizo es MASM32 en su versión 6

    ResponderEliminar
    Respuestas
    1. Por lo que veo SumePorReferencia es una función de suma, a la que se la pasa dos variables, una dword y un dato. Estos dos datos son enviados a través de pila mediante la macro Invoque. Yo no la uso nunca en este blog, porque como todo lo estandar hace que perdamos la realidad de lo que estamos haciendo. Yo prefiero hacerme una macro para cada función.
      Continuando. La función devuelve el resultado (un DWORD) en eax. Si ese resultado se lo pasas a la función udword$(Res), te funciona, proque esta función es un equivalente a la función "itoa" de "C". Es decir esta función convierte el número en una cadena, devolviendo el puntero al bufer resultante. por eso cuando usas el resultado que te da en MessageBox, te sale bien. Cuando llamas a Udword, te devuelve el puntero a una cadena, es decir un número, que es lo quec pide MessageBox.
      En cambio cuando lo haces directamente, estás pasando el valor de este putnero a Buffer, que es un buffer de 20 caracteres, no un número DWORD para ser usado como puntero. Estás mezclando dos conceptos,la cadena y el puntero a la cadena.
      Puedes hacer tres cosas.
      Usar udWord y guardar el eax devuelto en una variable buffer tipo dword, la cual usarás como puntero en el futuro.
      Copiar la conversión hecha por udword en tu buffer, mediante un bucle que copie todos los bytes desde el indicado en eax hasta que el valor sea cero sobre tu bufer.
      mov esi,eax
      copia:
      mov al,[esi]
      inc esi
      cmp al,0
      jnz copia

      Y por último te queda hacerte tu udword$ propia, que use como destino el bufer que le pidas.
      Yo no la tengo hecha para micros de 32 bits, pero aquí la tienes para el de 8086. El algoritmo es lo mismo. Esta funciónte convierte un número de 16 bits. Esta funcion que te pongo hace más que udword.
      http://myassembler.blogspot.com.es/search/label/itoa
      como udword usa un buffer interno, llamado numero, pero en vez de pasarte la dirección del bufer como hace udword, lo que yo hago es copiarte directamente la conversión sobre el destino que me iniques mediante un puntero llamado destino. Además en este caso se le paa una base de numeración, y podrá convertirte el número a cualquier base.
      El programa está hecho en ASM1.0 para formato ".com" si lo quieres en un ensamblador mas avanzado tendrás que reconvertir algunas directivas.
      Como en el blog he querido comenzar de cero, estas primeras funciones las desarrolle integras, sin usar directivas de alto nivel de ensamblador. por eso los parámetros de pila se pasan y declaran directamente, y los registros usados también se salvan y recuperan en el programa. Esto es la realidad de lo que se genera en ensamblador. Aquí tienes un ejemplo de la trnasformación.
      http://myassembler.blogspot.com.es/2013/08/pasamos-integrar-tasm-20.html
      Un saludo

      Eliminar
    2. Saludos, Gracias por tu respuesta, ya habia encontrado la solución a hacerlo, pero gracias a tu respuesta me aclaraste lo de la dirección de memoria.
      Hice mi propio procedimiento y aqui pongo la solución en dos partes para que sea mas entendible, eres libre si quieres de crear una entrada en tu blog con este contenido, y si quieres nombrarme te agradecería, y si no igual no importa. Aquí esta la solución como yo la vi:
      Parte 1:
      a.- Descubri que para conseguir el valor ascii de un numero, solo tenia que sumarle 48 a ese numero.
      b.- Para sumarselo debia descomponerlo, dado que el numero es base 10 se puede descomponer dividiendolo entre 10 sucesivamente hasta que el resultado es 0, en el resto de la división se encuentra el ultimo numero de el numero completo, la siguiente funcion lo que hace es tomar cualquier valor DWORD sin signo y descomponerlo, irle sumando al resto 48 y copiandolo en el buffer. He aquí el código fuente:
      La variable a se refiere al número y la b es un puntero a una cadena terminada en 0 que servira como buffer

      pStr proc a:DWORD, b:DWORD
      pusha ; Se guardan todos los registros en la pila
      mov ecx, b ; Se guarda la direccion de memoria del buffer en ;ecx
      mov eax, a ; Se guarda el valor del numero en eax
      mov edx,0 ; Inicializa el registro edx, Necesario para la división
      mov ebx, 10 ; Pone en ebx el divisor, se dividira el numero ;entre 10
      div ebx ; Divide el valor de eax (Numero) entre 10(ebx)
      mov ebp, 0 (Inicializa ebp (Lo he utilizado como contador, se ;podia incrementar el puntero en ecx, pero he decidido hacerlo asi)

      Inicio:
      .if eax != 0 ; Si el resultado de la división es distinto de cero
      add edx, 48 ; Suma 48 al resto de la división (edx)
      mov [ecx + ebp], edx ; Mueve a la dirección que apunta ;ecx + ebp el valor de la suma(osea ya esta en nuestro buffer el valor ;ascii del primer numero)
      inc ebp ;Incrementa ebp, para que ecx apunte una ;direccion mas alla en el buffer(el siguiente caracter en el buffer)
      jmp Division ; Salta a la etiqueta Division
      .else ;Si eax(Resultado de la division ) es = 0
      add edx, 48 ;Suma 48 al ultimo numero
      mov [ecx + ebp], edx ;Guarda el resultado en el buffer
      mov edx, 0 ; Mueve a edx el caracter NULL o fin de ;cadena
      mov [ecx + ebp + 1], edx ; Mueve al buffer el valor de fin ;de cadena
      jmp Fin ; Salta a la etiqueta fin
      .endif
      Fin:
      popa ;Restaura todos los valores de los registros
      ret ;Sale del procedimiento
      Division: ; Va descomponiendo el numero dado
      mov edx, 0
      mov ebx, 10
      div ebx
      jmp Inicio
      pStr endp


      El unico problema es que devuelve el numero al revés.
      Así que uso una función para invertir la cadena final.

      Parte 2:
      Invertir la cadena:

      revStr proc
      invertir_cadena:
      push ebp
      mov ebp, esp
      mov eax, [ebp+8]
      push 0

      cadena_en_pila:
      mov bl, [eax]
      test bl, bl
      jz fin_cadena_en_pila
      push bx
      inc eax
      jmp cadena_en_pila

      fin_cadena_en_pila:
      mov eax, [ebp + 8]

      invertir:
      pop bx
      mov [eax], bl
      inc eax
      test bl, bl
      jnz invertir
      leave
      ret

      revStr endp

      No la explico porque ya se me estan congelando los dedos, (estoy en un cyber :P)

      Luego junto las dos en una sola función y ya tengo la función a la que voy a llamar:

      intToStr proc dNumero:DWORD, pszBuffer:DWORD
      invoke pStr, dNumero, pszBuffer ; Convierte el numero pasado a ;ascii y lo devuelve al reves en el buffer
      push pszBuffer ;Pongo la dirección de memoria del buffer( que contiene el numero al revés, ya en modo texto ) en la pila
      invoke revStr ; Llamo a la función que invierte la cadena que le pase ;en la pila
      ret
      intToStr endp

      PD: La pude hacer mejor, pero la hice rápido. Asi que no tome en cuenta varias concideraciones, además me es suficiente para lo que yo queria.
      Luego voy a tratar de hacer una biblioteca de strings para MASM32.

      Saludos y que te vaya bien.

      Eliminar
    3. Gracias Enmanuel. La dejaré aquí, pues yo, como te dije ya tenía compuesta la rutina en el blog. En aunque es, como la tuya, para numeros de 32 bits está realizado con procesador de 16 bits.
      Mirala por si quieres tomar ideas para el futuro. Es mucho más completa, pues puede convertir el número a cualquiere sistema decimal incluido el Hexadecimal. Para ello, se usa una cadena de conversión. Esto es una cadena de caracteres, en que cada caracter representa el valor de la posición en que se encuentra en la cadena. Así se puede a interpretar valores por encima de 9 como el A,B,C y D del hexadecimal.
      Te lo voy a comentar con detalle, para que puedas entenderlo mejor.
      Mi función añade a lo que tu has hecho:
      - Convierte todas las bases
      - Interpreta el signo de los números, si el número es decimal
      - Funciona directamente sobre la cadena que se le indique
      - No necesita invertir la cadena al final de la interpretación.

      Tu has hecho bien la recolocación de la cadena ya que has usado la pila, pero no te has dado cuenta que la pila siempre estuvo ahí, y que es independiente del proceso. Por lo que, como yo, siempre pudiste usarla para ir almacenando los restos de las divisiones en ella, de forma que simplemente necesitabas recuperarlos luego. Te ahorras así el primer bucle de tu función de revStr.

      Utiliza la pila para hacerlo directamente al procesar.

      He realizado otra entrada con la misma rutina, pero usando las directivas uses u argumentos TASM para simplificar. Además he puesto lo que hay que hacer para reescribir el código en 32bits, tanto para procesar números de 32 com de 64 bits.
      En ensamblador es muy importante optimizar código.Verás que yo no hago mov dx,0, sino xor dx,dx. Parece una tontería pero ocupa menos byte, y la diferencia es aún mayor con un micro de 32 bits. Cuando usas xor dx,dx el es más corto y rápido, ya que no lleva datos. Su codigo máquina es más corto, por lo que ocupa menos memoria, y es mas rápido su ejecución por no tener que acceder a memoria para buscar el dato. En cambio hacen lo mismo.

      Versión literal ASM
      http://myassembler.blogspot.com/2013/06/entero-cadena.html

      Versión TASM2.0 16bits con interpretación poara 32bits
      http://myassembler.blogspot.com/2014/03/itoa.html

      Un saludo

      Eliminar
    4. Saludos, es cierto, tienes toda la razon, yo sabia que podia optimizarla mas pero soy un flojo :D, ademas aun no termino de entender por completo lo de la pila :/ es que vengo de un lenguaje de alto nivel, pero me fascina assembler. Tus rutinas estan de lujo. Gracias.
      Por cierto he visto que no haz creado ultimamente entradas en tu blog.
      Si hay algo que se mas o menos (Lo digo asi porque nunca se sabe suficiente) es programar en la API de Windows. Si quieres podria colaborarte escribiendo un artículo, y tu ves que tal y si te gusta lo puedes poner como entrada en tu blog.
      Cualquier cosa me mandas un MP.

      Eliminar
    5. Enmanuel te he escrito un MP, pero no se si lo has leido

      Eliminar
  7. hola, disculpa recien voy aprendiendo assembler, como puedo concatenar una cadena con una variable numerica, es decir que en el texto de la ventana salga "mi numero es ####" en flat assembler

    ResponderEliminar
    Respuestas
    1. Como casi todo en ensamblador, es solo cuestión de desglosar el problemas en problemas más pequeños. El ensamblador es un lenguaje que exige una grán disciplina de análisis.
      Tu preguntas: ¿Como puedo concatenar una cadena con una variable numérica?
      Miras el ensamblador y ves que puedes concatenar cadenas. "Me cachis", vaya no hay cadena y número.
      Pues desglosa el problema: Sumar cadenas ya lo tienes. Si el número vuera una cadena que lo representa, ya estaría... Pues hazlo. Convierte el número en la cadena alfanumérica que lo representa, y luego une estas cadenas.
      Cadena ""mi numero es". Número 1253.
      Si conviertes el número 1253 en la cadena "1253" solo tendrás que concatenar las dos cadenas.
      Para convertir un número en su cadena alfanumérica en ensamblador tienes que crearte una función. Esta función también la tenía el "ANSI C", y yo y mucha gente la llamamos como en C: "itoa" -> "integer to ascii"
      Esta función ya la tienes implementada en mi blog en http://myassembler.blogspot.com/2014/03/itoa.html
      El proceso por lo SRCAT, la función de esta entrada para obtener una cadena suma.
      Simple
      Un saludo

      Eliminar
  8. buenas recien voy aprendiendo assembler, como puedo concatenar una cadena con una variable numerica en flat assembler??

    ResponderEliminar

Si tienes algún comentario, duda o sugerencia, o si quieres aportar algún código creado a partir de las librerías expuestas aquí, por favor, indícamelo.